home *** CD-ROM | disk | FTP | other *** search
Wrap
/* * $Id: amiga.c 1.16 2000/12/25 11:41:23 olsen Exp olsen $ * * :ts=4 * * AmigaOS wrapper routines for Samba 2.0.0, using the AmiTCP V3 API * and the SAS/C V6.58 compiler. * * Copyright (C) 1999-2000 by Olaf `Olsen' Barthel <olsen@sourcery.han.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /******************************************************************************/ #include <bsdsocket/socketbasetags.h> #include <libraries/usergroup.h> #include <intuition/intuition.h> #include <libraries/locale.h> #include <exec/execbase.h> #include <exec/memory.h> #include <dos/dosextens.h> #include <dos/dostags.h> #include <dos/rdargs.h> #include <devices/timer.h> #include <utility/date.h> #include <clib/usergroup_protos.h> #include <clib/socket_protos.h> #include <clib/intuition_protos.h> #include <clib/utility_protos.h> #include <clib/locale_protos.h> #include <clib/timer_protos.h> #include <clib/alib_protos.h> #include <clib/exec_protos.h> #include <clib/dos_protos.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/dir.h> #include <net/if.h> #include <constructor.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <utime.h> #include <errno.h> #include <stdio.h> #include <fcntl.h> #include <inetd.h> #include <stat.h> #include <ios1.h> #include <dos.h> #include <pragmas/usergroup_pragmas.h> #include <pragmas/socket_pragmas.h> #include <pragmas/exec_sysbase_pragmas.h> #include <pragmas/intuition_pragmas.h> #include <pragmas/utility_pragmas.h> #include <pragmas/locale_pragmas.h> #include <pragmas/timer_pragmas.h> #include <pragmas/dos_pragmas.h> /******************************************************************************/ /* This is for the "iface_struct" definition. */ #include "interfaces.h" /******************************************************************************/ /*#define DEBUG*/ #include "Assert.h" /******************************************************************************/ STATIC VOID ReportProblem(const char *fmt, ...); STATIC VOID ForbidDOSCleanup(VOID); STATIC VOID PermitDOS(VOID); STATIC VOID ForbidDOS(VOID); STATIC VOID UnblockDescriptorCleanup(VOID); STATIC BOOL IsDescriptorNonblocking(int fd); STATIC VOID BlockDescriptor(int fd); STATIC VOID UnblockDescriptor(int fd); STATIC VOID SaveDescriptorCleanup(VOID); STATIC VOID RestoreDescriptor(struct UFB *ufb); STATIC BOOL SaveDescriptor(struct UFB *ufb); STATIC VOID UnmangleName(char **namePtr, struct MangleInfo *mi); STATIC int MangleName(char ** namePtr,struct MangleInfo * mi); STATIC VOID CloseUnlinkUnlockCleanup(VOID); STATIC VOID OpenDirCleanup(VOID); STATIC int TranslateRelativePath(char **namePtr, char *replace, int maxReplaceLen); STATIC VOID MapDescriptorSets(const fd_set *input_fds, int num_input_fds, fd_set *socket_fds, int *max_socket_fd_ptr, fd_set *file_fds, int *max_file_fd_ptr); STATIC VOID RemapDescriptorSets(const fd_set *socket_fds, int max_socket_fd, const fd_set *file_fds, int max_file_fd, fd_set *output_fds, int num_output_fds); STATIC VOID ConvertFileInfoToStat(struct MsgPort *port, struct FileInfoBlock *fib, struct stat *st); STATIC BOOL do_match(STRPTR str, STRPTR regexp); STATIC VOID nstrcpy_blank(const size_t maxSize, char *to, const char *from); STATIC BOOL SetFileSocket(FILE *stream, int sockfd); STATIC VOID DaemonInit(VOID); STATIC struct tm *ConvertTime(ULONG seconds); STATIC VOID MapIoErrToErrno(VOID); STATIC int MapFileNameAmigaToUnix(const char *amiga, char *unix, int maxUnixLen); STATIC VOID FlushSTDOUT(VOID); STATIC VOID CleanupSambaSemaphore(VOID); STATIC BOOL SetupSambaSemaphore(VOID); STATIC VOID RemoveLockedRegionNode(struct UFB *ufb, LONG start, LONG stop); STATIC VOID DeleteLockedRegionNode(struct LockedRegionNode *lrn); STATIC LONG CreateLockedRegionNode(struct LockedRegionNode **resultPtr); STATIC VOID DeleteFileLockNode(struct FileLockNode *fln); STATIC LONG CreateFileLockNode(struct UFB *ufb, struct FileLockNode **resultPtr); STATIC LONG FindFileLockNodeByFileHandle(BPTR fileHandle, struct FileLockNode **resultPtr); STATIC VOID FindFileLockNodeByDrawerAndName(BPTR parentDir, STRPTR fileName, struct FileLockNode **resultPtr); STATIC VOID CleanupFileLocks(int fd); STATIC int HandleFileLocking(int cmd, struct flock *l, struct UFB *ufb); STATIC struct LockedRegionNode *FindCollidingRegion(struct FileLockNode *fln, LONG start, LONG stop, BOOL shared); /******************************************************************************/ int amiga_sigmask(int signum); int amiga_sigblock(int sigmask); int amiga_sigsetmask(int sigmask); int amiga_unlink(char *name); int amiga_open(char *name, int mode, int prot); int amiga_chdir(char *path); DIR *amiga_opendir(char *dirName); VOID amiga_closedir(DIR *dir); struct dirent *amiga_readdir(DIR *dir); int amiga_mkdir(char *name, int mode); int amiga_rmdir(char *name); int amiga_creat(char *name, int prot); FILE *amiga_fopen(char *name, char *mode); int amiga_rename(char *old, char *new); char *amiga_getcwd(char *buf, size_t size); int amiga_ftruncate(int fd, off_t size); int amiga_accept(int sockfd, struct sockaddr *cliaddr, int *addrlen); int amiga_bind(int sockfd, struct sockaddr *name, int namelen); int amiga_close(int fd); int amiga_connect(int sockfd, struct sockaddr *name, int namelen); int amiga_getpeername(int sockfd, struct sockaddr *name, int *namelen); int amiga_getsockopt(int sockfd, int level, int optname, VOID *optval, int *optlen); int amiga_ioctl(int fd, unsigned long request, char *arg); int amiga_listen(int sockfd, int backlog); int amiga_read(int fd, VOID *data, unsigned int size); int amiga_recvfrom(int sockfd, VOID *buff, int len, int flags, struct sockaddr *from, int *fromlen); int amiga_select(int num_fds, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *timeout); int amiga_sendto(int sockfd, VOID *buff, int len, int flags, struct sockaddr *to, int tolen); int amiga_setsockopt(int sockfd, int level, int optname, VOID *optval, int optlen); int amiga_socket(int domain, int type, int protocol); int amiga_write(int fd, VOID *data, unsigned int size); int amiga_stat(char *name, struct stat *st); int amiga_lstat(char *name, struct stat *statstruct); int amiga_fstat(int fd, struct stat *st); int amiga_chmod(char *name, int mode); int amiga_dup(int fd); int amiga_dup2(int old_fd, int new_fd); int amiga_chown(char *name, uid_t uid, gid_t gid); int amiga_setegid(gid_t g); int amiga_seteuid(uid_t u); int amiga_gettimeofday(struct timeval *tv); int amiga_utime(char *name, struct utimbuf *time); VOID amiga_sleep(unsigned int seconds); char *amiga_crypt(char *key, char *salt); char *amiga_getpass(char *prompt); int amiga_setgid(gid_t id); int amiga_setgroups(int ngroups, gid_t *groups); gid_t amiga_getgid(VOID); struct group *amiga_getgrgid(gid_t gid); struct group *amiga_getgrnam(char *name); int amiga_getgroups(int ngroups, gid_t *groups); struct hostent *amiga_gethostbyaddr(char *addr, int len, int type); struct hostent *amiga_gethostbyname(char *name); struct netent *amiga_getnetbyname(char *name); int amiga_gethostname(char *hostname, int size); struct passwd *amiga_getpwnam(char *name); struct passwd *amiga_getpwuid(uid_t uid); uid_t amiga_getuid(VOID); gid_t amiga_getegid(VOID); uid_t amiga_geteuid(VOID); int amiga_initgroups(char *name, gid_t basegroup); int amiga_setuid(uid_t id); int amiga_umask(int mask); unsigned long amiga_inet_addr(char *addr); char *amiga_inet_ntoa(struct in_addr in); int amiga_getopt(int argc, char *argv[], char *opts); int amiga_system(char *cmd); int amiga_fork(VOID); VOID __tzset(VOID); time_t time(time_t *timeptr); struct tm *gmtime(const time_t *t); struct tm *localtime(const time_t *t); int amiga_strcasecmp(char *a, char *b); int amiga_strncasecmp(char *a, char *b, int len); VOID (*amiga_signal(int which,VOID (* action)(int)))(int); VOID amiga_alarm(int seconds); int amiga_waitpid(pid_t pid, int *status, int options); long amiga_setsid(VOID); int amiga_setreuid(uid_t real, uid_t eff); int amiga_setregid(gid_t real, gid_t eff); int amiga_fcntl(int fd, int cmd, unsigned long arg); int amiga_getsockname(int sockfd, struct sockaddr *name, int *namelen); int amiga_statfs(char *name, struct statfs *f); int amiga_execl(char *path, char *arg, ...); char *amiga_strerror(int error); int amiga_access(char *name, int modes); off_t amiga_lseek(int fd, off_t offset, int mode); int amiga_chroot(char *name); int amiga_kill(pid_t pid, int sig); pid_t amiga_getpid(VOID); int amiga_fgetc(FILE *in); char *amiga_fgets(char *str, int n, FILE *in); int amiga_fputs(const char *str, FILE *out); int amiga_puts(const char *str); int amiga_vfprintf(FILE *out, const char *fmt, va_list args); int amiga_fprintf(FILE *out, const char *fmt, ...); int amiga_printf(const char *fmt, ...); size_t amiga_fwrite(const void *data, size_t blockSize, size_t numBlocks, FILE *out); size_t amiga_fread(void *data, size_t blockSize, size_t numBlocks, FILE *in); int amiga_fclose(FILE *stream); int amiga_fflush(FILE *stream); int amiga_fseek(FILE *stream, long int offset, int mode); long int amiga_ftell(FILE *stream); int amiga_setvbuf(FILE *fp, char *buff, int type, size_t size); int amiga_fputc(int c,FILE *stream); VOID amiga_setbuf(FILE *stream,char *buffer); /******************************************************************************/ VOID __regargs __chkabort(VOID); VOID _CXOVF(VOID); VOID __regargs _CXBRK(VOID); /******************************************************************************/ /* These are in the Samba runtime library. */ extern int vsnprintf (char *str, size_t count, const char *fmt, va_list arg); extern int snprintf (char *str, size_t count, const char *fmt, ...); /******************************************************************************/ #define ZERO ((BPTR)0L) #define ERROR (-1) #define OK (0) #define SAME (0) #define NO ! #define NOT ! #define DONT ! #define CANNOT ! #define BUSY (NULL) #define NO_FLAG (0) /******************************************************************************/ #define FLAG_IS_SET(v,f) (((v) & (f)) == (f)) #define FLAG_IS_CLEAR(v,f) (((v) & (f)) == 0) #define SET_FLAG(v,f) (v |= (f)) #define CLEAR_FLAG(v,f) (v &= ~(f)) /******************************************************************************/ #define STRING_IS_EMPTY(s) (s[0] == '\0') #define NUM_ENTRIES(t) (sizeof(t) / sizeof(t[0])) /******************************************************************************/ #define FIB_IS_FILE(fib) ((fib)->fib_DirEntryType < 0) #define FIB_IS_DRAWER(fib) ((fib)->fib_DirEntryType >= 0 && \ (fib)->fib_DirEntryType != ST_SOFTLINK) /******************************************************************************/ /* This macro lets us long-align structures on the stack */ #define D_S(type,name) \ char a_##name[sizeof(type)+3]; \ type *name = (type *)((LONG)(a_##name+3) & ~3) /******************************************************************************/ #define UFB_IS_SOCKET 0x0800 #define UFB_IS_NON_BLOCKING 0x1000 #define UFB_UNLINK 0x2000 #define UFB_LOCKED 0x4000 #define UNIX_TIME_OFFSET 252482400 #define MAX_BSTR_LEN 256 #define MAX_FILENAME_LEN 512 #define MAX_FFS_NAME_LEN 30 /******************************************************************************/ /* This is for ReadArgs(), to make the parameters a little more readable. */ typedef unsigned char * KEY; typedef long SWITCH; typedef long * NUMBER; /******************************************************************************/ long __oslibversion = 37; /******************************************************************************/ extern struct Library * SysBase; extern struct Library * DOSBase; extern struct Library * UtilityBase; /******************************************************************************/ STATIC struct Device * TimerBase; STATIC struct MsgPort * TimerPort; STATIC struct timerequest * TimerRequest; STATIC LONG MinutesWest; /******************************************************************************/ STATIC struct Library * SocketBase; STATIC struct Library * UserGroupBase; /******************************************************************************/ /* Activate root mode, i.e. make getuid(), etc. return 0 instead of * the 'real' user id. */ STATIC BOOL RootMode = FALSE; /******************************************************************************/ #if defined(_M68020) && defined(_M68881) #define MACHINE_TYPE "['020+FPU]" #elif defined(_M68020) #define MACHINE_TYPE "['020]" #else #define MACHINE_TYPE "[68k]" #endif /******************************************************************************/ #include "Amiga_Samba_rev.h" char * VersionTag = VERSTAG " " MACHINE_TYPE " Samba version 2.0.0 ported by Olaf `Olsen' Barthel <olsen@sourcery.han.de>"; /******************************************************************************/ /* This is for backwards compatibility with Kickstart 2.04 and * avoids a deadlock when trying to get a shared lock on * a semaphore already held in exclusive mode by the same Task. */ STATIC VOID SafeObtainSemaphoreShared(struct SignalSemaphore * semaphore) { /* Do it right with Kickstart 3.x. */ if(SysBase->lib_Version >= 39) { ObtainSemaphoreShared(semaphore); } else { /* Try to get the shared semaphore */ if(CANNOT AttemptSemaphoreShared(semaphore)) { /* Check if we can get the exclusive version */ if(CANNOT AttemptSemaphore(semaphore)) { /* Oh well, wait for the shared lock */ ObtainSemaphoreShared(semaphore); } } } } /******************************************************************************/ STATIC BOOL AllowBreak = TRUE; STATIC BOOL CheckAbort(VOID) { BOOL result; result = (BOOL)(AllowBreak && FLAG_IS_SET(SetSignal(0,SIGBREAKF_CTRL_C),SIGBREAKF_CTRL_C)); return(result); } VOID __regargs __chkabort(VOID) { if(CheckAbort()) raise(SIGINT); } int amiga_sigmask(int signum) { int result = 0; ENTER(); SHOWVALUE(signum); if(signum == SIGTERM || signum == SIGINT) result = (1<<SIGINT); RETURN(result); return(result); } int amiga_sigblock(int sigmask) { BOOL oldAllowBreak = AllowBreak; int result = 0; ENTER(); SHOWVALUE(sigmask); if(DONT AllowBreak) result = (1<<SIGINT); if(FLAG_IS_SET(sigmask,(1<<SIGINT))) { AllowBreak = FALSE; if(oldAllowBreak != AllowBreak) { SocketBaseTags( SBTM_SETVAL(SBTC_BREAKMASK),NO_FLAG, TAG_END); } } RETURN(result); return(result); } int amiga_sigsetmask(int sigmask) { BOOL oldAllowBreak = AllowBreak; int result = 0; ENTER(); SHOWVALUE(sigmask); AllowBreak = FLAG_IS_CLEAR(sigmask,(1<<SIGINT)); if(oldAllowBreak != AllowBreak) { ULONG mask; if(AllowBreak) mask = SIGBREAKF_CTRL_C; else mask = NO_FLAG; SocketBaseTags( SBTM_SETVAL(SBTC_BREAKMASK),mask, TAG_END); } RETURN(result); return(result); } /******************************************************************************/ STATIC VOID ReportProblem(const char *fmt,...) { extern const STRPTR _ProgramName; BOOL useRequester = TRUE; va_list varArgs; ASSERT(fmt != NULL); va_start(varArgs,fmt); /* Launched from Workbench? */ if(WBenchMsg == NULL) { if(DOSBase->lib_Version >= 37) { BPTR stream; /* Make a copy of the current terminal * output stream. This avoids sending * an error message through a redirected * standard output stream, the output * will go straight to the terminal. */ stream = Open("CONSOLE:",MODE_NEWFILE); if(stream != ZERO) { struct FileHandle * fh = BADDR(stream); /* Check if the output was redirected * to "NIL:"; if not, print the error * error message. */ if(fh->fh_Type != NULL) { FPrintf(stream,"%s: ",_ProgramName); VFPrintf(stream,(STRPTR)fmt,(APTR)varArgs); FPrintf(stream,"\a\n"); useRequester = FALSE; } Close(stream); } } } /* Oh well, don't use the Shell for output. Put up a * requester. */ if(useRequester) { struct Library * IntuitionBase; IntuitionBase = OpenLibrary("intuition.library",37); if(IntuitionBase != NULL) { struct Window * reqWindow; struct EasyStruct es; char title[100]; snprintf(title,sizeof(title)-1,"Amiga Samba Error (%s)",FilePart(_ProgramName)); title[sizeof(title)-1] = '\0'; memset(&es,0,sizeof(es)); es.es_StructSize = sizeof(es); es.es_Title = (STRPTR)title; es.es_TextFormat = (STRPTR)fmt; es.es_GadgetFormat = "Ok"; reqWindow = BuildEasyRequestArgs(NULL,&es,0,(APTR)varArgs); if(reqWindow != NULL && reqWindow != (struct Window *)1) { struct timerequest * timeRequest = NULL; struct MsgPort * timePort; BOOL timerOpen = FALSE; ULONG windowSignal; ULONG timerSignal; ULONG signals; BOOL done; timePort = CreateMsgPort(); if(timePort != NULL) { timeRequest = CreateIORequest(timePort,sizeof(*timeRequest)); if(timeRequest != NULL) { if(OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)timeRequest,0) == OK) timerOpen = TRUE; } } windowSignal = (1UL << reqWindow->UserPort->mp_SigBit); if(timerOpen) { timerSignal = (1UL << timePort->mp_SigBit); timeRequest->tr_node.io_Command = TR_ADDREQUEST; timeRequest->tr_time.tv_secs = 30; timeRequest->tr_time.tv_micro = 0; SendIO((struct IORequest *)timeRequest); } else { timerSignal = 0; } DisplayBeep(reqWindow->WScreen); done = FALSE; do { signals = Wait(windowSignal | timerSignal); if(FLAG_IS_SET(signals,windowSignal)) { if(SysReqHandler(reqWindow,NULL,FALSE) >= 0) done = TRUE; } if(FLAG_IS_SET(signals,timerSignal)) { done = TRUE; } } while(NOT done); if(timerOpen) { if(CheckIO((struct IORequest *)timeRequest) == BUSY) AbortIO((struct IORequest *)timeRequest); WaitIO((struct IORequest *)timeRequest); CloseDevice((struct IORequest *)timeRequest); } DeleteIORequest((struct IORequest *)timeRequest); DeleteMsgPort(timePort); FreeSysRequest(reqWindow); } CloseLibrary(IntuitionBase); } } va_end(varArgs); } /******************************************************************************/ STATIC APTR OldWindowPtr; STATIC LONG ForbidCount; STATIC VOID ForbidDOSCleanup(VOID) { if(ForbidCount > 0) { struct Process * pr = (struct Process *)FindTask(NULL); pr->pr_WindowPtr = OldWindowPtr; ForbidCount = 0; } } STATIC VOID PermitDOS(VOID) { ASSERT(ForbidCount > 0); if(--ForbidCount == 0) { struct Process * pr = (struct Process *)FindTask(NULL); pr->pr_WindowPtr = OldWindowPtr; } } STATIC VOID ForbidDOS(VOID) { if(ForbidCount++ == 0) { struct Process * pr = (struct Process *)FindTask(NULL); OldWindowPtr = pr->pr_WindowPtr; pr->pr_WindowPtr = (APTR)-1; } } /******************************************************************************/ #define SINGLE_CHARACTER_MODE (1) #define BUFFERED_MODE (0) STATIC int MaxNonblockingDescriptor = -1; STATIC VOID UnblockDescriptorCleanup(VOID) { struct UFB * ufb; int fd; /* We look for descriptors we switched into * non-blocking mode and reset them back * to blocking mode. */ for(fd = 0 ; fd <= MaxNonblockingDescriptor ; fd++) { ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_NON_BLOCKING)) { SetMode((BPTR)ufb->ufbfh,BUFFERED_MODE); CLEAR_FLAG(ufb->ufbflg,UFB_IS_NON_BLOCKING); } } } STATIC BOOL IsDescriptorNonblocking(int fd) { struct UFB * ufb; BOOL result = FALSE; /* Verify if a descriptor was switched into non-blocking mode. */ ufb = chkufb(fd); if(ufb != NULL) result = FLAG_IS_SET(ufb->ufbflg,UFB_IS_NON_BLOCKING); return(result); } STATIC VOID BlockDescriptor(int fd) { struct UFB * ufb; /* This resets a file descriptor to blocking mode, * once it has been set to blocking mode. */ ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_NON_BLOCKING)) { SetMode((BPTR)ufb->ufbfh,BUFFERED_MODE); CLEAR_FLAG(ufb->ufbflg,UFB_IS_NON_BLOCKING); } } STATIC VOID UnblockDescriptor(int fd) { struct UFB * ufb; ufb = chkufb(fd); if(ufb != NULL) { /* Make sure we got a file and it's not already * in non-blocking mode. */ if(FLAG_IS_CLEAR(ufb->ufbflg,UFB_IS_SOCKET) && FLAG_IS_CLEAR(ufb->ufbflg,UFB_IS_NON_BLOCKING)) { /* Try to flip it into single character mode, * which we treat as "non-blocking" mode. */ if(SetMode((BPTR)ufb->ufbfh,SINGLE_CHARACTER_MODE)) { SET_FLAG(ufb->ufbflg,UFB_IS_NON_BLOCKING); /* Remember which one we switched. */ if(MaxNonblockingDescriptor < fd) MaxNonblockingDescriptor = fd; } } } } /******************************************************************************/ struct SavedDescriptorNode { struct MinNode sdn_MinNode; struct UFB * sdn_UFB; BPTR sdn_FileHandle; }; STATIC BOOL DescriptorListInitialized = FALSE; STATIC struct List DescriptorList; STATIC VOID SaveDescriptorCleanup(VOID) { /* This routine restores all file descriptors * we used to map to sockets to use proper file * handles. */ if(DescriptorListInitialized) { struct SavedDescriptorNode * sdn; for(sdn = (struct SavedDescriptorNode *)DescriptorList.lh_Head ; sdn->sdn_MinNode.mln_Succ != NULL ; sdn = (struct SavedDescriptorNode *)sdn->sdn_MinNode.mln_Succ) { /* Make sure that if this file is bound to * a socket, the socket is closed. */ if(FLAG_IS_SET(sdn->sdn_UFB->ufbflg,UFB_IS_SOCKET)) { CloseSocket(sdn->sdn_UFB->ufbfh); CLEAR_FLAG(sdn->sdn_UFB->ufbflg,UFB_IS_SOCKET); } sdn->sdn_UFB->ufbfh = sdn->sdn_FileHandle; } } } STATIC VOID RestoreDescriptor(struct UFB * ufb) { ASSERT(ufb != NULL); /* This routine restores one particular file descriptor * which was used as a socket to refer to a proper * file handle. */ if(DescriptorListInitialized) { struct SavedDescriptorNode * sdn; for(sdn = (struct SavedDescriptorNode *)DescriptorList.lh_Head ; sdn->sdn_MinNode.mln_Succ != NULL ; sdn = (struct SavedDescriptorNode *)sdn->sdn_MinNode.mln_Succ) { if(sdn->sdn_UFB == ufb) { sdn->sdn_UFB->ufbfh = sdn->sdn_FileHandle; Remove((struct Node *)sdn); free(sdn); break; } } } } STATIC BOOL SaveDescriptor(struct UFB * ufb) { struct SavedDescriptorNode * sdn; BOOL result = FALSE; ASSERT(ufb != NULL); /* Set up the descriptor list, unless it's already * initialized. */ if(NOT DescriptorListInitialized) { NewList(&DescriptorList); DescriptorListInitialized = TRUE; } sdn = malloc(sizeof(*sdn)); if(sdn != NULL) { memset(sdn,0,sizeof(*sdn)); /* Remember the file buffer and the file * handle attached to the descriptor. */ sdn->sdn_UFB = ufb; sdn->sdn_FileHandle = ufb->ufbfh; AddHead(&DescriptorList,(struct Node *)sdn); result = TRUE; } return(result); } /******************************************************************************/ STATIC BOOL InitialCurrentDirInitialized = FALSE; STATIC BPTR InitialCurrentDir; STATIC BPTR ChangedCurrentDir; CBMLIB_DESTRUCTOR(CloseLibsAndDevs) { ENTER(); /* Return to the directory we were in * when we started. */ if(InitialCurrentDirInitialized) { CurrentDir(InitialCurrentDir); InitialCurrentDirInitialized = FALSE; } /* If the current directory was changed, unlock it. */ if(ChangedCurrentDir != ZERO) { UnLock(ChangedCurrentDir); ChangedCurrentDir = ZERO; } CleanupSambaSemaphore(); if(TimerRequest != NULL) { if(TimerRequest->tr_node.io_Device != NULL) CloseDevice((struct IORequest *)TimerRequest); DeleteIORequest((struct IORequest *)TimerRequest); TimerRequest = NULL; } if(TimerPort != NULL) { DeleteMsgPort(TimerPort); TimerPort = NULL; } TimerBase = NULL; if(SocketBase != NULL) { CloseLibrary(SocketBase); SocketBase = NULL; } if(UserGroupBase != NULL) { CloseLibrary(UserGroupBase); UserGroupBase = NULL; } LEAVE(); } CBMLIB_CONSTRUCTOR(OpenLibsAndDevs) { extern STRPTR _ProgramName; BOOL sambaSemaphoreCreated; struct LocaleBase * LocaleBase; char *timerError = ""; BPTR sambaLock; UBYTE env_buffer[256]; int result = ERROR; SETPROGRAMNAME(_ProgramName); SETDEBUGLEVEL(0); ENTER(); SocketBase = OpenLibrary("bsdsocket.library",3); UserGroupBase = OpenLibrary("usergroup.library",1); if(SysBase->lib_Version >= 37) { TimerPort = CreateMsgPort(); if(TimerPort != NULL) { TimerRequest = (struct timerequest *)CreateIORequest(TimerPort,sizeof(*TimerRequest)); if(TimerRequest != NULL) { if(OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)TimerRequest,NULL) == OK) TimerBase = TimerRequest->tr_node.io_Device; else timerError = "opening \"timer.device\""; } else { timerError = "creating timer I/O request"; } } else { timerError = "creating timer message port"; } } /* Try to determine this machine's time zone. */ LocaleBase = (struct LocaleBase *)OpenLibrary("locale.library",38); if(LocaleBase != NULL) { struct Locale * locale; locale = OpenLocale(NULL); MinutesWest = locale->loc_GMTOffset; CloseLocale(locale); CloseLibrary((struct Library *)LocaleBase); } ForbidDOS(); /* Try to get a lock on the "Samba:" directory, * just to make sure the assignment is in place. */ sambaLock = Lock("Samba:",SHARED_LOCK); if(sambaLock != ZERO) UnLock(sambaLock); PermitDOS(); /* Initialize the global process ID database. */ sambaSemaphoreCreated = SetupSambaSemaphore(); if(SocketBase != NULL && UserGroupBase != NULL && TimerBase != NULL && sambaLock != ZERO && sambaSemaphoreCreated) { STATIC long h_errno = 0; struct timeval now; int error; error = SocketBaseTags( SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), &errno, SBTM_SETVAL(SBTC_HERRNOLONGPTR), &h_errno, SBTM_SETVAL(SBTC_LOGTAGPTR), _ProgramName, SBTM_SETVAL(SBTC_BREAKMASK), SIGBREAKF_CTRL_C, TAG_END); if(error != OK) { ReportProblem("Error initializing socket data (error=%ld).",error); goto out; } error = ug_SetupContextTags(_ProgramName, UGT_ERRNOPTR(sizeof(errno)),&errno, TAG_END); if(error != OK) { ReportProblem("Error initializing user group data (error=%ld).",error); goto out; } /* Use a new random number seed. */ GetSysTime((APTR)&now); srand(now.tv_secs); } else { if(SocketBase == NULL) ReportProblem("Error opening \"bsdsocket.library\" V3."); else if (UserGroupBase == NULL) ReportProblem("Error opening \"usergroup.library\" V1."); else if (TimerBase == NULL) ReportProblem("Error %s.",timerError); else if (sambaLock == ZERO) ReportProblem("Error finding \"Samba:\" assignment."); else if (NOT sambaSemaphoreCreated) ReportProblem("Error creating Samba semaphore."); goto out; } /* Now try to read any global options that may be set. */ if(GetVar("Samba",env_buffer,sizeof(env_buffer)-1,GVF_GLOBAL_ONLY) > 0) { struct RDArgs * rda; rda = (struct RDArgs *)AllocDosObjectTagList(DOS_RDARGS,NULL); if(rda != NULL) { struct { SWITCH RootMode; } cmd_args; STRPTR cmd_template = "ROOT/S"; strcat(env_buffer,"\n"); rda->RDA_Source.CS_Buffer = env_buffer; rda->RDA_Source.CS_Length = strlen(env_buffer); rda->RDA_Source.CS_CurChr = 0; rda->RDA_Flags |= RDAF_NOPROMPT; memset(&cmd_args,0,sizeof(cmd_args)); if(ReadArgs(cmd_template,(LONG *)&cmd_args,rda)) { if(cmd_args.RootMode) RootMode = TRUE; FreeArgs(rda); } FreeDosObject(DOS_RDARGS,rda); } } /* Initialize the current time zone variable. */ __tzset(); result = OK; out: RETURN(result); return(result); } /******************************************************************************/ DESTRUCTOR_P(SocketExit,501) { ENTER(); SaveDescriptorCleanup(); UnblockDescriptorCleanup(); CloseUnlinkUnlockCleanup(); ForbidDOSCleanup(); LEAVE(); } /******************************************************************************/ CONSTRUCTOR_P(DaemonInit,501) { ENTER(); DaemonInit(); RETURN(0); return(0); } /******************************************************************************/ struct MangleInfo { char mi_Substitute[MAX_FILENAME_LEN]; char * mi_OldName; }; STATIC VOID UnmangleName(char ** namePtr,struct MangleInfo * mi) { /* Reset the name pointer to its previous position. */ (*namePtr) = mi->mi_OldName; } STATIC int MangleName(char ** namePtr,struct MangleInfo * mi) { char * name = (*namePtr); int result = ERROR; ENTER(); SHOWSTRING(name); if(TranslateRelativePath(&name,mi->mi_Substitute,sizeof(mi->mi_Substitute)) == OK) { char * replace = mi->mi_Substitute; int len,i; SHOWSTRING(name); len = strlen(name); /* If there is one, strip the trailing slash. */ if(len > 1 && name[len-1] == '/') { SHOWMSG("trailing slash"); if(name != replace) { strcpy(replace,name); name = replace; } name[--len] = '\0'; } if(strcmp(name,".") == SAME) { SHOWMSG("dot file"); /* Convert to current directory. */ name = ""; } else if (strcmp(name,"..") == SAME) { SHOWMSG("dot dot file"); /* Convert to parent directory. */ name = "/"; } else if (strncmp(name,"./",2) == SAME) { SHOWMSG("reference to local dir"); /* Retain just the name. */ name += 2; } else if (strncmp(name,"../",3) == SAME) { SHOWMSG("reference to parent dir"); /* Convert to parent directory. */ name += 2; } else if (strncmp(name,"/",1) == SAME) { SHOWMSG("root dir"); /* Ok, so this is an absolute path. We first * check for a few special cases, the first * being a reference to "/tmp". */ if(Strnicmp(name,"/tmp",4) == SAME && (name[4] == '/' || name[4] == '\0')) { SHOWMSG("tmp"); if(name[4] == '/') { /* Convert "/tmp/foo" to "T:foo". */ strcpy(replace,"T:"); memmove(&replace[2],&name[5],strlen(&name[5])+1); } else { /* Convert "/tmp" to "T:". */ strcpy(replace,"T:"); } name = replace; } else if(Strnicmp(name,"/dev/",5) == SAME) { SHOWMSG("dev"); /* Except for "/dev/null" all references to * files in "/dev" are redirected to nonexistant * files. Note that this relies upon the fact * that "NIL:" is not a true device. */ if(Stricmp(name,"/dev/null") == SAME) name = "NIL:"; else name = "NIL:this_file_never_opens"; } else { int i,rest = 0,len = 0; SHOWMSG("absolute"); /* Find out how long the first component * of the absolute path is. */ for(i = 1 ; i <= strlen(name) ; i++) { if(name[i] == '/' || name[i] == '\0') { len = i-1; /* Is there anything following * the path name? */ if(name[i] == '/') rest = i+1; break; } } /* Copy the first component and * attach a colon. "/foo" becomes * "foo:" (without the trailing NUL * byte, this will get attached * later). */ memmove(replace,&name[1],len); replace[len++] = ':'; /* Now add the finishing touches. "/foo/bar" finally * becomes "foo:bar" and "/foo" becomes "foo:" with the * trailing NUL byte attached. */ if(rest > 0) memmove(&replace[len],&name[rest],strlen(&name[rest])+1); else replace[len] = '\0'; name = replace; } } SHOWSTRING(name); /* Convert any "./" or "../" embedded in the name * if necessary. */ SHOWMSG("stripping embedded slashes"); for(i = 0 ; i < strlen(name) ; i++) { if(strncmp(&name[i],"./",2) == SAME || strncmp(&name[i],"../",3) == SAME) { char * from = name; char * to = replace; name = replace; while((*from) != '\0') { if((*from) == '.') { if(from[1] == '/' || from[1] == '\0') { /* "." -> "" (done) * "./" -> "" (continue) */ if(from[1] == '\0') break; else from += 2; } else if (from[1] == '.' && (from[2] == '/' || from[2] == '\0')) { /* ".." -> "/" (done) * "../" -> "/" (continue) */ (*to++) = '/'; if(from[2] == '\0') break; else from += 3; } else { (*to++) = (*from++); } } else { (*to++) = (*from++); } } (*to) = '\0'; break; } } /* Reduce any "//" embedded in the name if * necessary. */ SHOWMSG("stripping more embedded slashes"); for(i = 0 ; i < ((int)strlen(name))-1 ; i++) { if(name[i] == '/' && name[i+1] == '/') { int position,len; if(name != replace) { strcpy(replace,name); name = replace; } len = strlen(name); position = len - 1; while(position > 1 && name[position] != ':') { /* "foo/bar//baz" -> "foo/baz" */ if(name[position] == '/' && name[position-1] == '/') { int componentLen; /* Move in front of the name component preceding the "//". */ componentLen = 0; for(i = position - 2 ; i > 0 && name[i] != ':' && name[i] != '/' ; i--) componentLen++; if(componentLen > 0) { memmove(&name[position - (componentLen + 1)],&name[position + 1],len - (position + 1)); len -= componentLen + 2; name[len] = '\0'; position -= componentLen + 1; } } position--; } break; } } D(("original name |%s|",(*namePtr))); D((" mangled name |%s|",name)); /* Look for extra colon characters * embedded in the name (as in "foo:bar:baz" * or "foo/bar:baz") which really don't * belong here. */ SHOWMSG("stripping colons"); len = strlen(name); for(i = 0 ; i < len ; i++) { if(name[i] == ':' || name[i] == '/') { int j; for(j = i+1 ; j < len ; j++) { if(name[j] == ':') { errno = EINVAL; /* invalid name */ goto out; } } break; } } /* Now check if the file name is longer than the * maximum supported by the ROM file system. This * is to avoid name space clashes. */ SHOWMSG("checking file name length"); if(strlen(FilePart((STRPTR)name)) > MAX_FFS_NAME_LEN) { LONG error = OK; BPTR fileLock; D(("name is probably too long (%ld > %ld)",strlen(FilePart((STRPTR)name)) > MAX_FFS_NAME_LEN)); /* This is a tricky issue: the filing system we are talking * to may be able to handle more than 30 characters, but then * it may be not, it's impossible to tell. We adopt the following * strategy: we try to access the named file and compare its * name against the one reported by the file system. If the name * reported by the file system is shorter than the one provided, * but matches it otherwise, we will assume that trouble is underway * and back out backwards. */ ForbidDOS(); fileLock = Lock((STRPTR)name,SHARED_LOCK); if(fileLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(fileLock,fib)) { STRPTR file_name = FilePart((STRPTR)name); int len; /* Now check if the name reported by the * filing system is shorter than the * one we asked for, but matches otherwise. */ len = strlen(fib->fib_FileName); if(strlen(file_name) > len && Strnicmp(file_name,fib->fib_FileName,len) == SAME) { /* The name is too long to handle. */ error = ERROR_LINE_TOO_LONG; } } else { error = IoErr(); } UnLock(fileLock); } else { error = IoErr(); } PermitDOS(); /* We don't complain if the file does * not exist, but if it is currently in * use, on a volume that's not currently * mounted, etc. we will complain. */ if(error != OK && error != ERROR_OBJECT_NOT_FOUND) { SetIoErr(error); MapIoErrToErrno(); goto out; } } mi->mi_OldName = (*namePtr); (*namePtr) = name; result = OK; } out: RETURN(result); return(result); } /******************************************************************************/ STATIC int MaxOpenDescriptor = -1; STATIC VOID CloseUnlinkUnlockCleanup(VOID) { struct UFB * ufb; int fd; ForbidDOS(); /* Don't let anybody stop us. */ signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN); /* We look for descriptors we marked for * deletion. */ for(fd = 0 ; fd <= MaxOpenDescriptor ; fd++) { CleanupFileLocks(fd); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_UNLINK)) { char fileName[MAX_FILENAME_LEN]; BOOL removeIt; removeIt = NameFromFH((BPTR)ufb->ufbfh,fileName,sizeof(fileName)); CLEAR_FLAG(ufb->ufbflg,UFB_UNLINK); close(fd); if(removeIt) DeleteFile(fileName); } } PermitDOS(); } int amiga_unlink(char *name) { struct MangleInfo mi; BPTR fileLock; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); if(MangleName(&name,&mi) == OK) { LONG error = OK; ForbidDOS(); fileLock = Lock(name,SHARED_LOCK); if(fileLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(fileLock,fib)) { UnLock(fileLock); fileLock = ZERO; /* Make sure that we get to remove * a file, as the name implies. */ if(FIB_IS_FILE(fib)) { if(DeleteFile(name)) result = OK; else error = IoErr(); } else { errno = EISDIR; } } else { error = IoErr(); } UnLock(fileLock); } else { error = IoErr(); } PermitDOS(); /* Check if we couldn't delete the file in * question because there still is a file * handle attached to it. If we can find that * file, we will mark it for deletion lateron * when the file is closed. */ if(result != OK && error == ERROR_OBJECT_IN_USE) { char parentDirName[MAX_FILENAME_LEN]; BOOL foundParentDirName; BPTR fileParentDir; int i; ASSERT(strlen(name) < sizeof(parentDirName)); strcpy(parentDirName,name); foundParentDirName = FALSE; for(i = ((int)strlen(parentDirName))-1 ; i >= 0 ; i--) { if(parentDirName[i] == ':') { if(parentDirName[i+1] != '\0') { parentDirName[i+1] = '\0'; foundParentDirName = TRUE; } break; } else if (parentDirName[i] == '/') { parentDirName[i] = '\0'; foundParentDirName = TRUE; break; } } /* Did we find this file's parent directory name? */ if(foundParentDirName) { D(("locking |%s|",parentDirName)); /* Get a lock on the file's parent directory. */ fileParentDir = Lock(parentDirName,SHARED_LOCK); if(fileParentDir != ZERO) { BPTR descriptorParentDir; BOOL gotIt = FALSE; struct UFB * ufb; int fd; for(fd = 0 ; fd <= MaxOpenDescriptor ; fd++) { ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_CLEAR(ufb->ufbflg,UFB_IS_SOCKET)) { /* And also get a lock on the file * descriptor's parent directory. */ descriptorParentDir = ParentOfFH((BPTR)ufb->ufbfh); if(descriptorParentDir != ZERO) { /* Are the two pointing to the same drawer? */ if(SameLock(fileParentDir,descriptorParentDir) == LOCK_SAME) { D_S(struct FileInfoBlock,fib); if(ExamineFH((BPTR)ufb->ufbfh,fib)) { /* Check if the two share the same name. */ if(Stricmp(fib->fib_FileName,FilePart(name)) == SAME) { /* Mark this file for deletion. */ SET_FLAG(ufb->ufbflg,UFB_UNLINK); gotIt = TRUE; error = OK; result = OK; } } else { SHOWMSG("couldn't examine the descriptor"); } } else { SHOWMSG("file and descriptor don't live in the same drawer"); } UnLock(descriptorParentDir); } else { SHOWMSG("the descriptor doesn't have a parent directory (huh?!?)"); } } if(gotIt) break; } UnLock(fileParentDir); } else { D(("couldn't get a lock on |%s|",parentDirName)); } } else { D(("didn't find parent dir name of |%s|",name)); } } /* Take care of the AmigaDOS error * code, if there is any. */ if(result != OK && error != OK) { SetIoErr(error); MapIoErrToErrno(); } UnmangleName(&name,&mi); } RETURN(result); return(result); } int amiga_open(char *name,int mode,int prot) { struct MangleInfo mi; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(mode); SHOWVALUE(prot); if(MangleName(&name,&mi) == OK) { /* Clear the "no delay" flag since the SAS/C * runtime library does not support it. */ CLEAR_FLAG(mode,O_NONBLOCK); ForbidDOS(); result = open(name,mode,prot); if(result != ERROR && MaxOpenDescriptor < result) MaxOpenDescriptor = result; PermitDOS(); UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ STATIC char CurrentDirName[MAX_FILENAME_LEN] = ""; int amiga_chdir(char *path) { BOOL isAbsolutePath; struct MangleInfo mi; int result = ERROR; chkabort(); ASSERT(path != NULL); ENTER(); SHOWSTRING(path); /* Is this an absolute path? */ isAbsolutePath = (BOOL)(path[0] == '/'); if(MangleName(&path,&mi) == OK) { BPTR drawerLock; D(("chdir(\"%s\")",path)); ForbidDOS(); drawerLock = Lock(path,SHARED_LOCK); if(drawerLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(drawerLock,fib)) { /* We can only move into drawers. */ if(FIB_IS_DRAWER(fib)) { /* If necessary, get the name of * the drawer to move into. */ if(NOT isAbsolutePath) { UBYTE localName[MAX_FILENAME_LEN]; if(NameFromLock(drawerLock,localName,sizeof(localName))) result = MapFileNameAmigaToUnix(localName,CurrentDirName,sizeof(CurrentDirName)); else MapIoErrToErrno(); } else { result = OK; } /* If everything went well, * move into the drawer. */ if(result == OK) { BPTR oldDrawer; oldDrawer = CurrentDir(drawerLock); /* Unlock the old drawer we came from * unless we want to return to it * when the program exits. */ if(InitialCurrentDirInitialized) { UnLock(oldDrawer); } else { InitialCurrentDir = oldDrawer; InitialCurrentDirInitialized = TRUE; } /* Make sure that this drawer is going * to be unlocked when the program exits. */ ChangedCurrentDir = drawerLock; } } else { errno = ENOTDIR; } } else { MapIoErrToErrno(); } if(result != OK) UnLock(drawerLock); } else { MapIoErrToErrno(); } PermitDOS(); UnmangleName(&path,&mi); if(result == OK && isAbsolutePath) strcpy(CurrentDirName,path); } RETURN(result); return(result); } /******************************************************************************/ struct OpenDirNode { struct MinNode odn_MinNode; BPTR odn_FileLock; struct FileInfoBlock odn_FIB; struct List odn_VolumeList; DIR odn_DIR; BOOL odn_ReadingVolumes; struct dirent odn_DirectoryEntry; struct Node * odn_NextNode; ULONG odn_NextDirEntryIndex; LONG odn_ParentDirKey; }; STATIC struct List OpenDirList; STATIC BOOL OpenDirListInitialized = FALSE; STATIC ULONG RootBlocks = 0; STATIC ULONG RootBlocksUsed = 0; STATIC VOID OpenDirCleanup(VOID) { struct OpenDirNode * odn; /* Unlock all directories still being scanned * when exit() was called. */ for(odn = (struct OpenDirNode *)OpenDirList.lh_Head ; odn->odn_MinNode.mln_Succ != NULL ; odn = (struct OpenDirNode *)odn->odn_MinNode.mln_Succ) { UnLock(odn->odn_FileLock); } } DIR * amiga_opendir(char *dirName) { char localDirName[MAX_FILENAME_LEN]; struct List volumeList; struct Node * node; DIR *result = NULL; chkabort(); ASSERT(dirName != NULL); ENTER(); SHOWSTRING(dirName); /* This is for buffering the list of available volumes. */ NewList(&volumeList); /* Make sure that the directory list is * set up properly. */ if(NOT OpenDirListInitialized) { NewList(&OpenDirList); OpenDirListInitialized = TRUE; atexit(OpenDirCleanup); } if(TranslateRelativePath(&dirName,localDirName,sizeof(localDirName)) == OK) { /* Check if we are to scan the virtual root directory. */ if(strcmp(dirName,"/") == SAME) { struct OpenDirNode * odn; odn = malloc(sizeof(*odn)); if(odn != NULL) { struct DosList * dol; RootBlocks = RootBlocksUsed = 0; memset(odn,0,sizeof(*odn)); odn->odn_ReadingVolumes = TRUE; odn->odn_DIR.dd_buf = (char *)odn; NewList(&odn->odn_VolumeList); AddHead(&OpenDirList,(struct Node *)odn); errno = OK; /* Now collect all volumes in the system. */ dol = NextDosEntry(LockDosList(LDF_VOLUMES|LDF_READ), LDF_VOLUMES|LDF_READ); while(dol != NULL) { /* Does the volume refer to a medium that is right * now present in the drive? */ if(dol->dol_Task != NULL) { STRPTR name; name = BADDR(dol->dol_Name); if(name != NULL && name[0] > 0) { LONG len; len = name[0]; node = malloc(sizeof(*node) + len + 2); if(node != NULL) { node->ln_Name = (char *)(node + 1); memcpy(node->ln_Name,name+1,len); node->ln_Name[len++] = ':'; node->ln_Name[len] = '\0'; AddTail(&volumeList,node); } else { errno = ENOMEM; break; } } } dol = NextDosEntry(dol,LDF_VOLUMES|LDF_READ); } UnLockDosList(LDF_VOLUMES|LDF_READ); /* If everything went well, proceed to examine every * volume previously added to the local volume list. * This procedure is necessary to avoid sending * packets to file systems while the DosList is * locked, which is a big NO-NO. */ if(errno == OK) { D_S(struct InfoData,id); struct MsgPort * task; result = &odn->odn_DIR; while((node = RemHead(&volumeList)) != NULL) { task = DeviceProc(node->ln_Name); if(task != NULL) { /* Is there a disk present? */ if(DoPkt(task,ACTION_DISK_INFO,MKBADDR(id), 0,0,0,0)) { /* Collect the number of blocks used and * available in our fake root directory. * Not that it matters much... */ if(id->id_BytesPerBlock == 512) { RootBlocks += id->id_NumBlocks; RootBlocksUsed += id->id_NumBlocksUsed; } else { RootBlocks += (id->id_NumBlocks * id->id_BytesPerBlock) / 512; RootBlocksUsed += (id->id_NumBlocksUsed * id->id_BytesPerBlock) / 512; } /* Remove the trailing colon character. */ node->ln_Name[strlen(node->ln_Name)-1] = '\0'; AddTail(&odn->odn_VolumeList,node); if(odn->odn_NextNode == NULL) odn->odn_NextNode = node; node = NULL; } } if(node != NULL) free(node); } } } else { errno = ENOMEM; } } else { struct MangleInfo mi; if(MangleName(&dirName,&mi) == OK) { BPTR fileLock; ForbidDOS(); fileLock = Lock(dirName,SHARED_LOCK); if(fileLock != ZERO) { struct OpenDirNode * odn; odn = malloc(sizeof(*odn)); if(odn != NULL) { BPTR parentDir; memset(odn,0,sizeof(*odn)); parentDir = ParentDir(fileLock); if(parentDir != ZERO) { if(Examine(parentDir,&odn->odn_FIB)) odn->odn_ParentDirKey = odn->odn_FIB.fib_DiskKey; UnLock(parentDir); } if(Examine(fileLock,&odn->odn_FIB)) { /* Make sure that we are * trying to read a drawer * and not a file. */ if(FIB_IS_DRAWER(&odn->odn_FIB)) { odn->odn_DIR.dd_buf = (char *)odn; odn->odn_FileLock = fileLock; NewList(&odn->odn_VolumeList); /* The lock has been "swallowed" * by the dir node; make sure that * we don't unlock it. */ fileLock = ZERO; AddHead(&OpenDirList,(struct Node *)odn); result = &odn->odn_DIR; } else { errno = ENOTDIR; } } else { MapIoErrToErrno(); } if(result == NULL) free(odn); } else { errno = ENOMEM; } UnLock(fileLock); } else { MapIoErrToErrno(); } PermitDOS(); UnmangleName(&dirName,&mi); } } } /* Get rid of any leftovers. */ while((node = RemHead(&volumeList)) != NULL) free(node); RETURN(result); return(result); } VOID amiga_closedir(DIR *dir) { ENTER(); if(dir != NULL) { struct OpenDirNode * odn; odn = (struct OpenDirNode *)dir->dd_buf; if(odn != NULL) { struct Node * node; Remove((struct Node *)odn); while((node = RemHead(&odn->odn_VolumeList)) != NULL) free(node); UnLock(odn->odn_FileLock); free(odn); } } chkabort(); LEAVE(); } struct dirent * amiga_readdir(DIR *dir) { struct dirent * result = NULL; ENTER(); chkabort(); if(dir != NULL) { struct OpenDirNode * odn; odn = (struct OpenDirNode *)dir->dd_buf; if(odn != NULL) { struct dirent * d = &odn->odn_DirectoryEntry; if(odn->odn_ReadingVolumes) { SHOWMSG("we are reading the virtual root directory"); /* The first directory entry points * back to the directory itself. */ if(odn->odn_NextDirEntryIndex == 0) { SHOWMSG("this is the first read attempt; returning the dot."); strcpy(d->d_name,"."); d->d_ino = ++odn->odn_NextDirEntryIndex; d->d_namlen = strlen(d->d_name); result = d; } else { /* Return the next volume in the list. */ if(odn->odn_NextNode != NULL && odn->odn_NextNode->ln_Succ != NULL) { ASSERT(sizeof(d->d_name) >= strlen(odn->odn_NextNode->ln_Name)); SHOWMSG("returning the next volume"); strcpy(d->d_name,odn->odn_NextNode->ln_Name); odn->odn_NextNode = odn->odn_NextNode->ln_Succ; d->d_ino = odn->odn_NextDirEntryIndex++; d->d_namlen = strlen(d->d_name); result = d; } else { errno = 0; } } } else { SHOWMSG("we are reading a user directory"); if(odn->odn_NextDirEntryIndex == 0) { SHOWMSG("returning '.'"); /* The first directory entry points * back to the directory itself. */ strcpy(d->d_name,"."); d->d_ino = odn->odn_FIB.fib_DiskKey; d->d_namlen = strlen(d->d_name); odn->odn_NextDirEntryIndex++; result = d; } else if(odn->odn_NextDirEntryIndex == 1) { SHOWMSG("returning '..'"); /* The second directory entry points * to the parent directory. */ strcpy(d->d_name,".."); d->d_ino = odn->odn_ParentDirKey; d->d_namlen = strlen(d->d_name); odn->odn_NextDirEntryIndex++; result = d; } else { SHOWMSG("returning next directory entry"); ForbidDOS(); /* All other iterations pick up the * next following directory entry. */ if(ExNext(odn->odn_FileLock,&odn->odn_FIB)) { ASSERT(sizeof(d->d_name) >= strlen(odn->odn_FIB.fib_FileName)); SHOWMSG("got another entry"); strcpy(d->d_name,odn->odn_FIB.fib_FileName); d->d_ino = odn->odn_FIB.fib_DiskKey; d->d_namlen = strlen(d->d_name); result = d; } else { LONG error = IoErr(); if(error == ERROR_NO_MORE_ENTRIES) { SHOWMSG("there is no next directory entry."); errno = 0; } else { SHOWMSG("some other problem..."); SetIoErr(error); MapIoErrToErrno(); } } PermitDOS(); } } } } if(result != NULL) SHOWSTRING(result->d_name); RETURN(result); return(result); } /******************************************************************************/ STATIC int TranslateRelativePath(char **namePtr,char *replace,int maxReplaceLen) { int result = ERROR; char * name; ENTER(); ASSERT(namePtr != NULL && (*namePtr) != NULL && replace != NULL); /* If we have a current directory all references should * be made relative to, do just that. Absolute paths * are not modified, though. */ name = (*namePtr); if(NOT STRING_IS_EMPTY(CurrentDirName) && name[0] != '/') { int totalLen; SHOWMSG("Changing the directory name"); /* Skip current dir modifiers, we just want the name. */ if(strncmp(name,"./",2) == SAME) name += 2; else if (strcmp(name,".") == SAME) name = ""; /* Get the current directory name and get * ready to attach the file name at the end. */ totalLen = strlen(CurrentDirName); if(CurrentDirName[totalLen-1] != '/' && CurrentDirName[totalLen-1] != ':' && NOT STRING_IS_EMPTY(name)) { totalLen++; } totalLen += strlen(name); /* Check if the complete string will fit. */ if(totalLen < maxReplaceLen) { /* Put the file name together. */ strcpy(replace,CurrentDirName); if(CurrentDirName[strlen(CurrentDirName)-1] != '/' && NOT STRING_IS_EMPTY(name)) strcat(replace,"/"); strcat(replace,name); (*namePtr) = replace; SHOWSTRING(*namePtr); result = OK; } else { errno = ENAMETOOLONG; } } else { result = OK; } RETURN(result); return(result); } /******************************************************************************/ int amiga_mkdir(char *name,int mode) { struct MangleInfo mi; BPTR dirLock; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(mode); if(MangleName(&name,&mi) == OK) { ForbidDOS(); dirLock = CreateDir((STRPTR)name); if(dirLock != ZERO) { UnLock(dirLock); result = OK; } else { MapIoErrToErrno(); } PermitDOS(); UnmangleName(&name,&mi); } if(result == OK) result = amiga_chmod(name,mode); RETURN(result); return(result); } /******************************************************************************/ int amiga_rmdir(char *name) { struct MangleInfo mi; BPTR fileLock; int result = ERROR; chkabort(); ENTER(); SHOWSTRING(name); if(MangleName(&name,&mi) == OK) { ForbidDOS(); fileLock = Lock(name,SHARED_LOCK); if(fileLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(fileLock,fib)) { UnLock(fileLock); fileLock = ZERO; /* Make sure that we get to remove a drawer, * as the function name implies. */ if(FIB_IS_DRAWER(fib)) { if(DeleteFile(name)) result = OK; else MapIoErrToErrno(); } else { errno = ENOTDIR; } } else { MapIoErrToErrno(); } UnLock(fileLock); } else { MapIoErrToErrno(); } PermitDOS(); UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ int amiga_creat(char *name,int prot) { struct MangleInfo mi; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(prot); if(MangleName(&name,&mi) == OK) { ForbidDOS(); result = creat(name,prot); PermitDOS(); UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ FILE * amiga_fopen(char *name,char *mode) { struct MangleInfo mi; FILE *result = NULL; chkabort(); ASSERT(name != NULL && mode != NULL); ENTER(); SHOWSTRING(name); SHOWSTRING(mode); if(MangleName(&name,&mi) == OK) { ForbidDOS(); result = fopen(name,mode); PermitDOS(); UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ int amiga_rename(char *old,char *new) { struct MangleInfo old_mi; struct MangleInfo new_mi; int result = ERROR; chkabort(); ASSERT(old != NULL && new != NULL); ENTER(); SHOWSTRING(old); SHOWSTRING(new); /* rename() causes the link named <from> to be renamed as <to>. If <to> exists, * it is first removed. Both <from> and <to> must be of the same type (that is, * both directories or both non-directories), and must reside on the same * file system. */ if(MangleName(&old,&old_mi) == OK) { if(MangleName(&new,&new_mi) == OK) { ForbidDOS(); D(("rename |%s| to |%s|",old,new)); if(CANNOT Rename(old,new)) { LONG error = IoErr(); SHOWVALUE(error); if(error == ERROR_OBJECT_EXISTS) { BPTR oldLock = Lock(old,SHARED_LOCK); BPTR newLock = Lock(new,SHARED_LOCK); if(oldLock != ZERO && newLock != ZERO && SameLock(oldLock,newLock) == LOCK_SAME) { result = OK; error = OK; SHOWMSG("Ok; same name"); UnLock(oldLock); UnLock(newLock); } else { UnLock(oldLock); UnLock(newLock); if(DeleteFile(new)) { if(Rename(old,new)) { result = OK; error = OK; SHOWMSG("Ok; after removing"); } else { error = IoErr(); } } else { error = IoErr(); } } } if(error != OK) { SHOWVALUE(error); SetIoErr(error); MapIoErrToErrno(); } } else { SHOWMSG("Ok"); result = OK; } PermitDOS(); UnmangleName(&new,&new_mi); } else { SHOWMSG("MangleName new_mi failed"); } UnmangleName(&old,&old_mi); } else { SHOWMSG("MangleName old_mi failed"); } RETURN(result); return(result); } /******************************************************************************/ char * amiga_getcwd(char *buf, size_t size) { char *result = NULL; chkabort(); ASSERT(buf != NULL); ENTER(); SHOWVALUE(buf); SHOWVALUE(size); if(CurrentDirName[0] == '/') { strncpy(buf,CurrentDirName,size-1); buf[size-1] = '\0'; result = buf; } else { BPTR oldDir; ForbidDOS(); oldDir = CurrentDir(ZERO); if(NameFromLock(oldDir,buf,size)) { if(MapFileNameAmigaToUnix(buf,buf,size) == OK) result = buf; } else { MapIoErrToErrno(); } CurrentDir(oldDir); PermitDOS(); } SHOWSTRING(result); RETURN(result); return(result); } /******************************************************************************/ #define SET_FILESIZE_ERROR (-1) int amiga_ftruncate(int fd,off_t size) { struct UFB * ufb; int result = ERROR; chkabort(); ENTER(); SHOWVALUE(fd); SHOWVALUE(size); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_CLEAR(ufb->ufbflg,UFB_IS_SOCKET)) { ForbidDOS(); if(SetFileSize((BPTR)ufb->ufbfh,size,OFFSET_BEGINNING) != SET_FILESIZE_ERROR) result = OK; else MapIoErrToErrno(); PermitDOS(); } else { errno = EBADF; } RETURN(result); return(result); } /******************************************************************************/ int amiga_accept(int sockfd,struct sockaddr *cliaddr,int *addrlen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(cliaddr != NULL && addrlen != NULL); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(cliaddr); SHOWVALUE(addrlen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = accept(ufb->ufbfh,cliaddr,(LONG *)addrlen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_bind(int sockfd,struct sockaddr *name,int namelen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(name); SHOWVALUE(namelen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = bind(ufb->ufbfh,name,namelen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_close(int fd) { struct UFB * ufb; int result = ERROR; chkabort(); ENTER(); SHOWVALUE(fd); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { result = CloseSocket(ufb->ufbfh); CLEAR_FLAG(ufb->ufbflg,UFB_IS_SOCKET); /* Make sure that the descriptor looks like a file again. */ RestoreDescriptor(ufb); close(fd); } else { if(ufb != NULL) { /* Don't close the stdio streams! */ if(fd == 0 || fd == 1 || fd == 2) { D(("Attempt to muck with fd #%ld!",fd)); errno = EBADF; } else { char fileName[MAX_FILENAME_LEN]; BOOL removeIt = FALSE; if(FLAG_IS_SET(ufb->ufbflg,UFB_UNLINK)) { removeIt = NameFromFH((BPTR)ufb->ufbfh,fileName,sizeof(fileName)); CLEAR_FLAG(ufb->ufbflg,UFB_UNLINK); } CleanupFileLocks(fd); result = close(fd); if(removeIt) DeleteFile(fileName); } } else { errno = EBADF; } } RETURN(result); return(result); } /******************************************************************************/ int amiga_connect(int sockfd,struct sockaddr *name,int namelen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(name != NULL && namelen > 0); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(name); SHOWVALUE(namelen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = connect(ufb->ufbfh,name,namelen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_getpeername(int sockfd,struct sockaddr *name,int *namelen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(name != NULL && namelen != NULL); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(name); SHOWVALUE(namelen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = getpeername(ufb->ufbfh,name,(LONG *)namelen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_getsockopt(int sockfd,int level,int optname,VOID *optval,int *optlen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(optval != NULL && optlen != NULL); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(level); SHOWVALUE(optname); SHOWVALUE(optval); SHOWVALUE(optlen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = getsockopt(ufb->ufbfh,level,optname,optval,(LONG *)optlen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_ioctl(int fd,unsigned long request,char *arg) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(arg != NULL); ENTER(); SHOWVALUE(fd); SHOWVALUE(request); SHOWVALUE(arg); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = IoctlSocket(ufb->ufbfh,request,arg); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_listen(int sockfd,int backlog) { struct UFB * ufb; int result = ERROR; chkabort(); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(backlog); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = listen(ufb->ufbfh,backlog); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_read(int fd,VOID *data,unsigned int size) { struct UFB * ufb; int result; chkabort(); ASSERT(data != NULL); ENTER(); SHOWVALUE(fd); SHOWVALUE(data); SHOWVALUE(size); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { SHOWMSG("input from socket"); result = recv(ufb->ufbfh,data,size,0); } else { SHOWMSG("input from file"); ForbidDOS(); result = read(fd,data,size); PermitDOS(); } RETURN(result); return(result); } /******************************************************************************/ int amiga_recvfrom(int sockfd,VOID *buff,int len,int flags,struct sockaddr *from,int *fromlen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(buff != NULL && from != NULL && fromlen != NULL); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(buff); SHOWVALUE(len); SHOWVALUE(flags); SHOWVALUE(from); SHOWVALUE(fromlen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = recvfrom(ufb->ufbfh,buff,len,flags,from,(LONG *)fromlen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ STATIC VOID MapDescriptorSets( const fd_set * input_fds, int num_input_fds, fd_set * socket_fds, int * max_socket_fd_ptr, fd_set * file_fds, int * max_file_fd_ptr) { FD_ZERO(socket_fds); FD_ZERO(file_fds); /* This routine maps file descriptor sets * from one format to another. We map * socket descriptors and regular file * descriptor sets. */ if(input_fds != NULL && num_input_fds > 0) { int max_socket_fd = (*max_socket_fd_ptr); int max_file_fd = (*max_file_fd_ptr); struct UFB * ufb; int i; for(i = 0 ; i < num_input_fds ; i++) { if(FD_ISSET(i,input_fds)) { D(("fd to wait on #%ld",i)); ufb = chkufb(i); if(ufb != NULL) { /* Is this a socket descriptor? */ if(FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { D(("fd #%ld is a socket.\n",i)); FD_SET(ufb->ufbfh,socket_fds); if(max_socket_fd < ufb->ufbfh) max_socket_fd = ufb->ufbfh; } else { D(("fd #%ld is a file.\n",i)); /* We only watch files bound to * console streams. */ if(IsInteractive((BPTR)ufb->ufbfh)) { FD_SET(i,file_fds); if(max_file_fd < i) max_file_fd = i; } } } } } (*max_socket_fd_ptr) = max_socket_fd; (*max_file_fd_ptr) = max_file_fd; } } STATIC VOID RemapDescriptorSets( const fd_set * socket_fds, int max_socket_fd, const fd_set * file_fds, int max_file_fd, fd_set * output_fds, int num_output_fds) { /* This routine reverses the mapping established * above. We map the file and socket descriptor * sets back into the original set. */ if(output_fds != NULL) { int fd; FD_ZERO(output_fds); for(fd = 0 ; fd <= max_socket_fd ; fd++) { if(FD_ISSET(fd,socket_fds)) { struct UFB * ufb; int output_fd; for(output_fd = 0 ; output_fd < num_output_fds ; output_fd++) { ufb = chkufb(output_fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET) && ufb->ufbfh == fd) { D(("fd #%ld has data",output_fd)); FD_SET(output_fd,output_fds); break; } } } } for(fd = 0 ; fd <= max_file_fd ; fd++) { if(FD_ISSET(fd,file_fds)) { D(("fd #%ld has data",fd)); FD_SET(fd,output_fds); } } } } int amiga_select(int num_fds,fd_set *read_fds,fd_set *write_fds,fd_set *except_fds,struct timeval *timeout) { fd_set socket_read_fds; fd_set socket_write_fds; fd_set socket_except_fds; int max_socket_fd; fd_set file_read_fds; fd_set file_write_fds; fd_set file_except_fds; struct UFB * ufb; int max_file_fd; int result = 0; chkabort(); ENTER(); if(num_fds > FD_SETSIZE) { D(("Warning: %ld descriptor sets to wait on requested; only %ld available.",num_fds,FD_SETSIZE)); num_fds = FD_SETSIZE; } max_socket_fd = -1; max_file_fd = -1; MapDescriptorSets(read_fds, num_fds, &socket_read_fds, &max_socket_fd, &file_read_fds, &max_file_fd); MapDescriptorSets(write_fds, num_fds, &socket_write_fds, &max_socket_fd, &file_write_fds, &max_file_fd); MapDescriptorSets(except_fds, num_fds, &socket_except_fds, &max_socket_fd, &file_except_fds, &max_file_fd); D(("number of socket fds to work on == %ld",max_socket_fd+1)); D(("number of file fds to work on == %ld",max_file_fd+1)); /* Wait for socket input? */ if(max_socket_fd != -1) { /* Wait for file input, too? */ if(max_file_fd != -1 && (timeout == NULL || timeout->tv_secs > 0 || timeout->tv_micro > 0)) { struct timeval stopWhen; struct timeval zero; BOOL gotSomething; ULONG breakMask; int i; /* We are going to poll all streams; for the timeout * feature to work, we absolutely must know when to * stop polling. * * Why aren't we using asynchronous DOS packets? * The answer is that once a packet is sent, you * cannot easily abort it. Polling is safer in * that respect. Yes, I know that ACTION_STACK * can be used to fake input to a console stream, * but I'd rather not rely upon it. */ if(timeout != NULL) { GetSysTime((APTR)&stopWhen); AddTime((APTR)&stopWhen,(APTR)timeout); } else { /* No timeout, poll until we are interrupted * or get input from any of the files. It's * not really necessary to initialize this * timeval, but it keeps the compiler happy. */ memset(&stopWhen,0,sizeof(stopWhen)); } while(TRUE) { /* Check for break signal. */ chkabort(); /* Delay for a tick to avoid busy-waiting. */ Delay(1); /* This tells WaitSelect() to poll the sockets for input. */ zero.tv_secs = 0; zero.tv_micro = 0; /* Signals to stop on; we want to stop when a break signal arrives. */ if(AllowBreak) breakMask = SIGBREAKF_CTRL_C; else breakMask = 0; /* Check for socket input. */ result = WaitSelect(max_socket_fd+1,&socket_read_fds,&socket_write_fds,&socket_except_fds,&zero,&breakMask); /* Stop if a break signal arrives. */ if((result < 0 && errno == EINTR) || FLAG_IS_SET(breakMask,SIGBREAKF_CTRL_C)) raise(SIGINT); /* Stop if the return value from WaitSelect is negative. */ if(result < 0) break; /* Did we get any socket input? */ gotSomething = (BOOL)(result > 0); if(NOT gotSomething) { /* Check all files for input. We also poll * them for input, but each with a little * delay of about 1/50 of a second. We stop * as soon as we find one file that has * input in it. */ for(i = 0 ; i <= max_file_fd ; i++) { if(FD_ISSET(i,&file_read_fds) || FD_ISSET(i,&file_write_fds) || FD_ISSET(i,&file_except_fds)) { ufb = chkufb(i); if(ufb != NULL) { if(WaitForChar((BPTR)ufb->ufbfh,1)) { gotSomething = TRUE; break; } } } } } /* Did we get any input at all? */ if(gotSomething) { BOOL gotInput; /* Now retest all files and remember * those that had input. */ for(i = 0 ; i <= max_file_fd ; i++) { gotInput = FALSE; if(FD_ISSET(i,&file_read_fds) || FD_ISSET(i,&file_write_fds) || FD_ISSET(i,&file_except_fds)) { ufb = chkufb(i); if(ufb != NULL) { /* Does this one have input? */ gotInput = WaitForChar((BPTR)ufb->ufbfh,1); } } if(gotInput) { /* Mark one more descriptor as * having input. */ result++; } else { /* Mark this descriptor as * not having any input. */ FD_CLR(i,&file_read_fds); FD_CLR(i,&file_write_fds); FD_CLR(i,&file_except_fds); } } } /* Did we get any input? If so, stop polling. */ if(result > 0) break; /* If a timeout was set, check if we are already * beyond the point of time when we should have * stopped polling. */ if(timeout != NULL) { struct timeval now; GetSysTime((APTR)&now); if((-CmpTime((APTR)&now,(APTR)&stopWhen)) >= 0) break; } } } else { ULONG breakMask; if(AllowBreak) breakMask = SIGBREAKF_CTRL_C; else breakMask = 0; result = WaitSelect(max_socket_fd+1,&socket_read_fds,&socket_write_fds,&socket_except_fds,timeout,&breakMask); if((result < 0 && errno == EINTR) || FLAG_IS_SET(breakMask,SIGBREAKF_CTRL_C)) raise(SIGINT); } } else { /* Wait for file input? */ if(max_file_fd != -1 && (timeout == NULL || timeout->tv_secs > 0 || timeout->tv_micro > 0)) { struct timeval stopWhen; BOOL gotSomething; int i; if(timeout != NULL) { GetSysTime((APTR)&stopWhen); AddTime((APTR)&stopWhen,(APTR)timeout); } else { memset(&stopWhen,0,sizeof(stopWhen)); } while(TRUE) { chkabort(); Delay(1); gotSomething = FALSE; for(i = 0 ; i <= max_file_fd ; i++) { if(FD_ISSET(i,&file_read_fds) || FD_ISSET(i,&file_write_fds) || FD_ISSET(i,&file_except_fds)) { ufb = chkufb(i); if(ufb != NULL) { if(WaitForChar((BPTR)ufb->ufbfh,1)) { gotSomething = TRUE; break; } } } } if(gotSomething) { BOOL gotInput; for(i = 0 ; i <= max_file_fd ; i++) { gotInput = FALSE; if(FD_ISSET(i,&file_read_fds) || FD_ISSET(i,&file_write_fds) || FD_ISSET(i,&file_except_fds)) { ufb = chkufb(i); if(ufb != NULL) { /* Does this one have input? */ gotInput = WaitForChar((BPTR)ufb->ufbfh,1); } } if(gotInput) { result++; } else { FD_CLR(i,&file_read_fds); FD_CLR(i,&file_write_fds); FD_CLR(i,&file_except_fds); } } } if(result > 0) break; if(timeout != NULL) { struct timeval now; GetSysTime((APTR)&now); if((-CmpTime((APTR)&now,(APTR)&stopWhen)) >= 0) break; } } } } /* The descriptor sets remain unchanged in * case of error. */ if(result >= 0) { RemapDescriptorSets(&socket_read_fds, max_socket_fd, &file_read_fds, max_file_fd, read_fds, num_fds); RemapDescriptorSets(&socket_write_fds, max_socket_fd, &file_write_fds, max_file_fd, write_fds, num_fds); RemapDescriptorSets(&socket_except_fds, max_socket_fd, &file_except_fds, max_file_fd, except_fds, num_fds); } RETURN(result); return(result); } /******************************************************************************/ int amiga_sendto(int sockfd,VOID *buff,int len,int flags,struct sockaddr *to,int tolen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(buff != NULL && to != NULL); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(buff); SHOWVALUE(len); SHOWVALUE(flags); SHOWVALUE(to); SHOWVALUE(tolen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = sendto(ufb->ufbfh,buff,len,flags,to,tolen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_setsockopt(int sockfd,int level,int optname,VOID *optval,int optlen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(optval != NULL); ENTER(); SHOWVALUE(sockfd); SHOWVALUE(level); SHOWVALUE(optname); SHOWVALUE(optval); SHOWVALUE(optlen); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = setsockopt(ufb->ufbfh,level,optname,optval,optlen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_socket(int domain,int type,int protocol) { int result = ERROR; int fd; chkabort(); ENTER(); SHOWVALUE(domain); SHOWVALUE(type); SHOWVALUE(protocol); /* We open a regular file that is guaranteed to * open in any case and then attach a socket * in place of the original file handle. */ fd = open("NIL:",O_RDWR,0777); if(fd != -1) { struct UFB * ufb; ufb = chkufb(fd); if(ufb != NULL) { /* Save the original file handle value. */ if(SaveDescriptor(ufb)) { int sockfd; /* Now create the real socket. */ sockfd = socket(domain,type,protocol); if(sockfd != -1) { /* Put the socket in place of the file handle. */ SET_FLAG(ufb->ufbflg,UFB_IS_SOCKET); ufb->ufbfh = sockfd; result = fd; } else { close(fd); } } else { close(fd); errno = ENOMEM; } } else { close(fd); errno = EBADF; } } RETURN(result); return(result); } /******************************************************************************/ int amiga_write(int fd,VOID *data,unsigned int size) { struct UFB * ufb; int result; chkabort(); ASSERT(data != NULL); ENTER(); SHOWVALUE(fd); SHOWVALUE(data); SHOWVALUE(size); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { result = send(ufb->ufbfh,data,size,0); } else { ForbidDOS(); result = write(fd,data,size); PermitDOS(); } RETURN(result); return(result); } /******************************************************************************/ STATIC VOID ConvertFileInfoToStat( struct MsgPort * port, struct FileInfoBlock * fib, struct stat * st) { ULONG flags; int mode; long time; /* This routine converts the contents of a FileInfoBlock * into information to fill a Unix-like stat data structure * with. */ flags = fib->fib_Protection ^ (FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE); if(FIB_IS_DRAWER(fib)) mode = S_IFDIR; else mode = S_IFREG; if(FLAG_IS_SET(flags,FIBF_READ)) SET_FLAG(mode,S_IRUSR); if(FLAG_IS_SET(flags,FIBF_WRITE) && FLAG_IS_SET(flags,FIBF_DELETE)) SET_FLAG(mode,S_IWUSR); if(FLAG_IS_SET(flags,FIBF_EXECUTE)) SET_FLAG(mode,S_IXUSR); if(FLAG_IS_SET(flags,FIBF_GRP_READ)) SET_FLAG(mode,S_IRGRP); if(FLAG_IS_SET(flags,FIBF_GRP_WRITE) && FLAG_IS_SET(flags,FIBF_GRP_DELETE)) SET_FLAG(mode,S_IWGRP); if(FLAG_IS_SET(flags,FIBF_GRP_EXECUTE)) SET_FLAG(mode,S_IXGRP); if(FLAG_IS_SET(flags,FIBF_OTR_READ)) SET_FLAG(mode,S_IROTH); if(FLAG_IS_SET(flags,FIBF_OTR_WRITE) && FLAG_IS_SET(flags,FIBF_OTR_DELETE)) SET_FLAG(mode,S_IWOTH); if(FLAG_IS_SET(flags,FIBF_OTR_EXECUTE)) SET_FLAG(mode,S_IXOTH); time = fib->fib_Date.ds_Days * 24*60*60 + fib->fib_Date.ds_Minute * 60 + (fib->fib_Date.ds_Tick / TICKS_PER_SECOND); memset(st,0,sizeof(*st)); st->st_dev = (u_long)port; st->st_ino = fib->fib_DiskKey; st->st_mode = mode; st->st_mtime = UNIX_TIME_OFFSET + time + 60*MinutesWest; /* translate from local time to UTC */ st->st_atime = st->st_mtime; st->st_ctime = st->st_mtime; st->st_uid = fib->fib_OwnerUID; st->st_gid = fib->fib_OwnerGID; if(FIB_IS_FILE(fib)) { st->st_nlink = 1; st->st_size = fib->fib_Size; } else { st->st_nlink = 2; } } int amiga_stat(char *name, struct stat *st) { char localName[MAX_FILENAME_LEN]; struct MangleInfo mi; int result = ERROR; chkabort(); ASSERT(name != NULL && st != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(st); if(TranslateRelativePath(&name,localName,sizeof(localName)) == OK) { char * originalName = name; if(MangleName(&name,&mi) == OK) { int len; SHOWSTRING(name); SHOWSTRING(originalName); len = strlen(name); if((strcmp(originalName,"/") == SAME) || (len > 1 && name[len-2] == ':' && name[len-1] == '/')) { struct timeval now; SHOWMSG("this is the virtual root directory"); /* This must be our virtual root directory. * Make something up. */ memset(st,0,sizeof(*st)); GetSysTime((APTR)&now); /* Nobody may write to this "directory". */ st->st_dev = (u_long)"Virtual Root Directory"; st->st_ino = 1; st->st_mode = S_IFDIR|S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; st->st_nlink = 2; st->st_mtime = UNIX_TIME_OFFSET + now.tv_secs + 60*MinutesWest; /* translate from local time to UTC */ st->st_atime = st->st_mtime; st->st_ctime = st->st_mtime; result = OK; } else { BPTR fileLock; SHOWMSG("this is a drawer or a file"); ForbidDOS(); fileLock = Lock((STRPTR)name,SHARED_LOCK); if(fileLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(fileLock,fib)) { BPTR parentDir; /* Check if this is the root directory. */ parentDir = ParentDir(fileLock); if(parentDir != ZERO) { /* This is not the root directory. */ UnLock(parentDir); } else { /* So this is the root directory. Make sure * that we return proper protection bits for * it, i.e. that the directory is always * readable, writable, etc. This may be * necessary since on the Amiga, root * directories cannot have any protection * bits set. Note that the "deletable" * bits don't make much sense, but then * these bits work together with the * writable bits. The lowest four bits * remain zero, which enables them all. */ fib->fib_Protection = FIBF_OTR_READ | FIBF_OTR_WRITE | FIBF_OTR_EXECUTE | FIBF_OTR_DELETE | FIBF_GRP_READ | FIBF_GRP_WRITE | FIBF_GRP_EXECUTE | FIBF_GRP_DELETE; } ConvertFileInfoToStat(((struct FileLock *)BADDR(fileLock))->fl_Task,fib,st); result = OK; } else { MapIoErrToErrno(); } UnLock(fileLock); } else { LONG error = IoErr(); if(error == ERROR_OBJECT_IN_USE) { char parentName[MAX_FILENAME_LEN]; BPTR parentLock; strcpy(parentName,name); (*PathPart(parentName) = '\0'); parentLock = Lock(parentName,SHARED_LOCK); if(parentLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(parentLock,fib)) { STRPTR onlyFileName = FilePart(name); while(ExNext(parentLock,fib)) { if(Stricmp(fib->fib_FileName,onlyFileName) == SAME) { ConvertFileInfoToStat(((struct FileLock *)BADDR(parentLock))->fl_Task,fib,st); result = OK; error = OK; break; } } } UnLock(parentLock); } } if(error != OK) { SetIoErr(error); MapIoErrToErrno(); } } PermitDOS(); } if(result == OK) { SHOWVALUE(st->st_dev); SHOWVALUE(st->st_ino); SHOWVALUE(st->st_size); SHOWVALUE(st->st_nlink); SHOWVALUE(st->st_mode); SHOWVALUE(st->st_mtime); SHOWVALUE(st->st_atime); SHOWVALUE(st->st_ctime); SHOWVALUE(st->st_uid); SHOWVALUE(st->st_gid); } else { SHOWVALUE(errno); } UnmangleName(&name,&mi); } } RETURN(result); return(result); } int amiga_lstat(char *name, struct stat *statstruct) { int result; chkabort(); ENTER(); SHOWSTRING(name); SHOWVALUE(statstruct); result = amiga_stat(name,statstruct); RETURN(result); return(result); } int amiga_fstat(int fd,struct stat * st) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(st != NULL); ENTER(); SHOWVALUE(fd); SHOWVALUE(st); ufb = chkufb(fd); if(ufb != NULL) { if(FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { long value; long size = sizeof(value); memset(st,0,sizeof(*st)); st->st_dev = (u_long)SocketBase; st->st_mode = S_IFSOCK | S_IRUSR | S_IWUSR; st->st_uid = geteuid(); st->st_gid = getegid(); if(getsockopt(fd,SOL_SOCKET,SO_SNDBUF,&value,&size) == 0) st->st_blksize = value; result = OK; } else { struct FileHandle * fileHandle = BADDR(ufb->ufbfh); /* Make sure that this stream doesn't * really refer to "NIL:". */ if(fileHandle->fh_Type != NULL) { D_S(struct FileInfoBlock,fib); ForbidDOS(); if(ExamineFH(ufb->ufbfh,fib)) { ConvertFileInfoToStat(fileHandle->fh_Type,fib,st); result = OK; } else { MapIoErrToErrno(); } PermitDOS(); } else { struct timeval tv; /* Make up some phony data for a NIL: file handle. */ memset(st,0,sizeof(*st)); GetSysTime((APTR)&tv); st->st_dev = (u_long)0; st->st_mode = S_IFREG | S_IRUSR | S_IWUSR; st->st_mtime = UNIX_TIME_OFFSET + tv.tv_secs + 60*MinutesWest; /* translate from local time to UTC */ st->st_atime = st->st_mtime; st->st_ctime = st->st_mtime; result = OK; } } } else { errno = EBADF; } RETURN(result); return(result); } /******************************************************************************/ int amiga_chmod(char *name,int mode) { struct MangleInfo mi; ULONG flags = 0; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(mode); /* Convert the file access modes into * Amiga typical protection bits. */ if(FLAG_IS_SET(mode,S_IRUSR)) SET_FLAG(flags,FIBF_READ); if(FLAG_IS_SET(mode,S_IWUSR)) { SET_FLAG(flags,FIBF_WRITE); SET_FLAG(flags,FIBF_DELETE); } if(FLAG_IS_SET(mode,S_IXUSR)) SET_FLAG(flags,FIBF_EXECUTE); if(FLAG_IS_SET(mode,S_IRGRP)) SET_FLAG(flags,FIBF_GRP_READ); if(FLAG_IS_SET(mode,S_IWGRP)) { SET_FLAG(flags,FIBF_GRP_WRITE); SET_FLAG(flags,FIBF_GRP_DELETE); } if(FLAG_IS_SET(mode,S_IXGRP)) SET_FLAG(flags,FIBF_GRP_EXECUTE); if(FLAG_IS_SET(mode,S_IROTH)) SET_FLAG(flags,FIBF_OTR_READ); if(FLAG_IS_SET(mode,S_IWOTH)) { SET_FLAG(flags,FIBF_OTR_WRITE); SET_FLAG(flags,FIBF_OTR_DELETE); } if(FLAG_IS_SET(mode,S_IXOTH)) SET_FLAG(flags,FIBF_OTR_EXECUTE); flags ^= (FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE); if(MangleName(&name,&mi) == OK) { ForbidDOS(); if(SetProtection(name,flags)) { result = OK; } else { LONG error = IoErr(); /* Note sure about this one; is it really that important * that the file attribute change succeeds? This is a definite * FIXME. */ if(error == ERROR_OBJECT_IN_USE) { result = OK; } else { SetIoErr(error); MapIoErrToErrno(); } } PermitDOS(); UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ int amiga_dup(int fd) { struct UFB * ufb; int result = ERROR; chkabort(); ENTER(); SHOWVALUE(fd); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { fd = open("NIL:",O_RDWR,0777); if(fd != -1) { struct UFB * ufb2; ufb2 = chkufb(fd); if(ufb2 != NULL) { if(SaveDescriptor(ufb2)) { int sockfd; sockfd = Dup2Socket(ufb->ufbfh,-1); if(sockfd != -1) { SET_FLAG(ufb2->ufbflg,UFB_IS_SOCKET); ufb2->ufbfh = sockfd; result = fd; } else { close(fd); } } else { close(fd); errno = ENOMEM; } } else { close(fd); errno = EBADF; } } } else { errno = ENOTSOCK; } RETURN(result); return(result); } /******************************************************************************/ int amiga_dup2(int old_fd,int new_fd) { struct UFB * old_ufb; struct UFB * new_ufb; int result = ERROR; chkabort(); ENTER(); SHOWVALUE(old_fd); SHOWVALUE(new_fd); old_ufb = chkufb(old_fd); new_ufb = chkufb(new_fd); if(old_ufb != NULL && FLAG_IS_SET(old_ufb->ufbflg,UFB_IS_SOCKET) && new_ufb != NULL) { int sockfd; if(FLAG_IS_SET(new_ufb->ufbflg,UFB_IS_SOCKET)) { CloseSocket(new_ufb->ufbfh); sockfd = Dup2Socket(old_ufb->ufbfh,new_ufb->ufbfh); } else { if(SaveDescriptor(new_ufb)) { sockfd = Dup2Socket(old_ufb->ufbfh,-1); } else { sockfd = -1; errno = ENOMEM; } } if(sockfd != -1) { SET_FLAG(new_ufb->ufbflg,UFB_IS_SOCKET); new_ufb->ufbfh = sockfd; result = new_fd; } } else { errno = ENOTSOCK; } RETURN(result); return(result); } /******************************************************************************/ struct bcpl_name { unsigned char name[MAX_BSTR_LEN]; }; int amiga_chown(char *name,uid_t uid,gid_t gid) { struct MangleInfo mi; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(uid); SHOWVALUE(gid); if(MangleName(&name,&mi) == OK) { /* AmigaDOS 3.0 and up have a SetOwner() call. */ if(DOSBase->lib_Version >= 39) { if(SetOwner(name,(((LONG)uid) << 16) | gid)) result = OK; else MapIoErrToErrno(); } else { struct DevProc * dvp; /* For 2.04 and 2.1 we'll have to do this * manually... */ dvp = GetDeviceProc(name,NULL); if(dvp != NULL) { D_S(struct bcpl_name,newName); int len; len = strlen(name); if(len > sizeof(newName->name)-1) len = sizeof(newName->name)-1; newName->name[0] = len; strncpy(&newName->name[1],name,len); /* This is almost identical in operation to ACTION_SET_PROTECTION. */ if(DoPkt(dvp->dvp_Port,ACTION_SET_OWNER,dvp->dvp_Lock,MKBADDR(newName),(((LONG)uid) << 16) | gid,0,0)) result = OK; else MapIoErrToErrno(); FreeDeviceProc(dvp); } else { MapIoErrToErrno(); } } UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ int amiga_setegid(gid_t g) { int result; chkabort(); ENTER(); SHOWVALUE(g); result = setregid(-1,g); RETURN(result); return(result); } /******************************************************************************/ int amiga_seteuid(uid_t u) { int result; chkabort(); ENTER(); SHOWVALUE(u); result = setreuid(-1,u); RETURN(result); return(result); } /******************************************************************************/ int amiga_gettimeofday(struct timeval *tv) { chkabort(); ENTER(); SHOWVALUE(tv); if(tv != NULL) { GetSysTime((APTR)tv); tv->tv_secs += UNIX_TIME_OFFSET + 60*MinutesWest; /* translate from local time to UTC */ SHOWVALUE(tv->tv_secs); SHOWVALUE(tv->tv_micro); } RETURN(0); return(0); } /******************************************************************************/ int amiga_utime(char *name,struct utimbuf *time) { struct MangleInfo mi; struct DateStamp ds; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(time); /* Use the current time? */ if(time == NULL) { DateStamp(&ds); } else { ULONG seconds; /* Convert the time given. */ if(time->modtime < (UNIX_TIME_OFFSET + 60*MinutesWest)) seconds = 0; else seconds = time->modtime - (UNIX_TIME_OFFSET + 60*MinutesWest); /* translate from UTC to local time */ ds.ds_Days = (seconds / (24*60*60)); ds.ds_Minute = (seconds % (24*60*60)) / 60; ds.ds_Tick = (seconds % 60) * TICKS_PER_SECOND; } if(MangleName(&name,&mi) == OK) { ForbidDOS(); if(SetFileDate((STRPTR)name,&ds)) { result = OK; } else { LONG error = IoErr(); /* Note sure about this one; is it really that important * that the file date change succeeds? This is a definite * FIXME. */ if(error == ERROR_OBJECT_IN_USE) { result = OK; } else { SetIoErr(error); MapIoErrToErrno(); } } PermitDOS(); UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ VOID amiga_sleep(unsigned int seconds) { chkabort(); ENTER(); SHOWVALUE(seconds); if(seconds > 0) { ULONG timerSignal = (1UL << TimerPort->mp_SigBit); ULONG signalsReceived; ULONG signalsToWaitFor; TimerRequest->tr_node.io_Command = TR_ADDREQUEST; TimerRequest->tr_time.tv_secs = seconds; TimerRequest->tr_time.tv_micro = 0; SetSignal(0,timerSignal); SendIO((struct IORequest *)TimerRequest); signalsToWaitFor = timerSignal; if(AllowBreak) signalsToWaitFor |= SIGBREAKF_CTRL_C; signalsReceived = Wait(signalsToWaitFor); /* Did we get a break signal while we were sleeping? */ if(FLAG_IS_SET(signalsReceived,SIGBREAKF_CTRL_C)) { if(CheckIO((struct IORequest *)TimerRequest) == BUSY) AbortIO((struct IORequest *)TimerRequest); WaitIO((struct IORequest *)TimerRequest); /* And pull the brakes... */ raise(SIGINT); } /* Proper termination. */ if(FLAG_IS_SET(signalsReceived,timerSignal)) { WaitIO((struct IORequest *)TimerRequest); } } LEAVE(); } /******************************************************************************/ char * amiga_crypt(char *key,char *salt) { char *result; chkabort(); ASSERT(key != NULL && salt != NULL); ENTER(); SHOWSTRING(key); SHOWVALUE(salt); result = crypt(key,salt); RETURN(result); return(result); } /******************************************************************************/ char * amiga_getpass(char *prompt) { char *result; chkabort(); ENTER(); SHOWSTRING(prompt); result = getpass(prompt); RETURN(result); return(result); } /******************************************************************************/ int amiga_setgid(gid_t id) { int result; chkabort(); ENTER(); SHOWVALUE(id); result = setgid(id); RETURN(result); return(result); } /******************************************************************************/ int amiga_setgroups(int ngroups,gid_t *groups) { int result; chkabort(); ASSERT(groups != NULL); ENTER(); SHOWVALUE(ngroups); SHOWVALUE(groups); result = setgroups(ngroups,groups); RETURN(result); return(result); } /******************************************************************************/ gid_t amiga_getgid(VOID) { int result; chkabort(); ENTER(); if(RootMode) result = 0; else result = getgid(); RETURN(result); return(result); } /******************************************************************************/ struct group * amiga_getgrgid(gid_t gid) { struct group *result; chkabort(); ENTER(); SHOWVALUE(gid); result = getgrgid(gid); RETURN(result); return(result); } /******************************************************************************/ struct group * amiga_getgrnam(char * name) { struct group *result; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); result = getgrnam(name); RETURN(result); return(result); } /******************************************************************************/ int amiga_getgroups(int ngroups, gid_t *groups) { int result; chkabort(); ASSERT(groups != NULL); ENTER(); SHOWVALUE(ngroups); SHOWVALUE(groups); result = getgroups(ngroups,groups); RETURN(result); return(result); } /******************************************************************************/ struct hostent * amiga_gethostbyaddr(char *addr, int len, int type) { struct hostent *result; chkabort(); ASSERT(addr != NULL); ENTER(); SHOWVALUE(addr); SHOWVALUE(len); SHOWVALUE(type); result = gethostbyaddr(addr,len,type); RETURN(result); return(result); } /******************************************************************************/ struct hostent * amiga_gethostbyname(char *name) { struct hostent *result; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); result = gethostbyname(name); RETURN(result); return(result); } /******************************************************************************/ struct netent * amiga_getnetbyname(char *name) { struct netent *result; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); result = getnetbyname(name); RETURN(result); return(result); } /******************************************************************************/ int amiga_gethostname(char *hostname,int size) { int result; chkabort(); ASSERT(hostname != NULL); ENTER(); SHOWVALUE(hostname); SHOWVALUE(size); result = gethostname(hostname,size); if(result == 0) SHOWSTRING(hostname); RETURN(result); return(result); } /******************************************************************************/ struct passwd * amiga_getpwnam(char *name) { struct passwd *result; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); result = getpwnam(name); RETURN(result); return(result); } /******************************************************************************/ struct passwd * amiga_getpwuid(uid_t uid) { struct passwd *result; chkabort(); ENTER(); SHOWVALUE(uid); result = getpwuid(uid); RETURN(result); return(result); } /******************************************************************************/ uid_t amiga_getuid(VOID) { uid_t result; chkabort(); ENTER(); if(RootMode) result = 0; else result = getuid(); RETURN(result); return(result); } /******************************************************************************/ gid_t amiga_getegid(VOID) { gid_t result; chkabort(); ENTER(); result = getegid(); RETURN(result); return(result); } /******************************************************************************/ uid_t amiga_geteuid(VOID) { uid_t result; chkabort(); ENTER(); result = geteuid(); RETURN(result); return(result); } /******************************************************************************/ int amiga_initgroups(char *name, gid_t basegroup) { int result; chkabort(); ASSERT(name != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(basegroup); result = initgroups(name,basegroup); RETURN(result); return(result); } /******************************************************************************/ int amiga_setuid(uid_t id) { int result; chkabort(); ENTER(); SHOWVALUE(id); result = setuid(id); RETURN(result); return(result); } /******************************************************************************/ int amiga_umask(int mask) { int result; chkabort(); ENTER(); SHOWVALUE(mask); result = umask(mask); RETURN(result); return(result); } /******************************************************************************/ unsigned long amiga_inet_addr(char *addr) { unsigned long result; chkabort(); ASSERT(addr != NULL); ENTER(); SHOWSTRING(addr); result = inet_addr(addr); RETURN(result); return(result); } /******************************************************************************/ char * amiga_inet_ntoa(struct in_addr in) { char *result; chkabort(); ENTER(); SHOWVALUE(in.s_addr); result = Inet_NtoA(in.s_addr); SHOWSTRING(result); RETURN(result); return(result); } /******************************************************************************/ int opterr = 1; int optind = 1; int optopt; char * optarg; int amiga_getopt(int argc, char * argv[], char *opts) { STATIC int sp = 1; int c; char *cp; ASSERT(argc > 0 && argv != NULL && opts != NULL); if(sp == 1) { if(optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') { return(EOF); } else if(strcmp(argv[optind], "--") == NULL) { optind++; return(EOF); } } optopt = c = argv[optind][sp]; if(c == ':' || (cp=index(opts, c)) == NULL) { if(opterr) fprintf(stderr, "%s%s%c\n", argv[0], ": illegal option -- ", c); if(argv[optind][++sp] == '\0') { optind++; sp = 1; } return('?'); } if(*++cp == ':') { if(argv[optind][sp+1] != '\0') { optarg = &argv[optind++][sp+1]; } else if(++optind >= argc) { if(opterr) fprintf(stderr, "%s%s%c\n", argv[0], ": option requires an argument -- ", c); sp = 1; return('?'); } else { optarg = argv[optind++]; } sp = 1; } else { if(argv[optind][++sp] == '\0') { sp = 1; optind++; } optarg = NULL; } return(c); } /******************************************************************************/ /* This comes from util/lib.c */ STATIC BOOL do_match(STRPTR str, STRPTR regexp) { STRPTR p; for(p = regexp; (*p) != '\0' && (*str) != '\0' ; NULL) { switch(*p) { case '?': str++; p++; break; /* Look for a character matching the one after the '*' */ case '*': p++; if((*p) == '\0') return(TRUE); /* Automatic match */ while((*str) != '\0') { while((*str) != '\0' && ToUpper(*p) != ToUpper(*str)) str++; if(do_match(str,p)) return(TRUE); if((*str) == '\0') return(FALSE); else str++; } return(FALSE); default: if(ToUpper(*str++) != ToUpper(*p++)) return(FALSE); break; } } if((*p) == '\0' && (*str) == '\0') return(TRUE); if((*p) == '\0' && str[0] == '.' && str[1] == '\0') return(TRUE); if((*str) == '\0' && (*p) == '?') { while((*p) == '?') p++; return((BOOL)((*p) == '\0')); } if((*str) == '\0' && ((*p) == '*' && p[1] == '\0')) return(TRUE); return(FALSE); } STATIC VOID nstrcpy_blank(const size_t maxSize,char *to,const char *from) { size_t len = 0; while((*from) != '\0' && (*from) != ' ' && len < maxSize-1) { (*to++) = (*from++); len++; } (*to) = '\0'; } STATIC VOID fill_volume_list(struct List * list) { struct DosList * dol; dol = NextDosEntry(LockDosList(LDF_VOLUMES|LDF_READ), LDF_VOLUMES|LDF_READ); while(dol != NULL) { /* Does the volume refer to a medium that is right * now present in the drive? */ if(dol->dol_Task != NULL) { STRPTR name = BADDR(dol->dol_Name); struct Node * node; int len; len = name[0]; node = malloc(sizeof(*node) + len+1); if(node != NULL) { /* Copy the name of the volume. */ node->ln_Name = (char *)(node + 1); strncpy(node->ln_Name,&name[1],len); node->ln_Name[len] = '\0'; AddTail(list,node); } } dol = NextDosEntry(dol,LDF_VOLUMES|LDF_READ); } UnLockDosList(LDF_VOLUMES|LDF_READ); /* Now that we have collected all device nodes, check * whether the volumes are usable. */ if(NOT IsListEmpty(list)) { char localName[MAX_BSTR_LEN+1]; D_S(struct InfoData,id); struct MsgPort * task; struct Node * node; struct Node * next; int len; for(node = list->lh_Head ; node->ln_Succ != NULL ; node = next) { next = node->ln_Succ; len = strlen(node->ln_Name); strncpy(localName,node->ln_Name,len); localName[len++] = ':'; localName[len] = '\0'; task = DeviceProc(localName); if(task == NULL || CANNOT DoPkt(task,ACTION_DISK_INFO,MKBADDR(id), 0,0,0,0)) { Remove(node); free(node); } } } } STATIC int deep_scan_drawer(char * drawerName,char * whichPattern,FILE * out) { char * originalDrawerName; struct MangleInfo mi; int result = ERROR; originalDrawerName = drawerName; if(MangleName(&drawerName,&mi) == OK) { BPTR dirLock; dirLock = Lock(drawerName,SHARED_LOCK); if(dirLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(dirLock,fib)) { struct AnchorPath * ap; ap = malloc(sizeof(*ap) + MAX_FILENAME_LEN); if(ap != NULL) { BOOL stopped = FALSE; BPTR oldDir; LONG error; oldDir = CurrentDir(dirLock); memset(ap,0,sizeof(*ap) + MAX_FILENAME_LEN); ap->ap_Strlen = MAX_FILENAME_LEN; ap->ap_BreakBits = SIGBREAKF_CTRL_C; if((error = MatchFirst("",ap)) == OK) { BOOL check; do { D(("checking |%s|",ap->ap_Buf)); check = TRUE; if(FIB_IS_FILE(&ap->ap_Info)) { SHOWMSG("this is a file"); } else { SHOWMSG("this is a drawer"); if(FLAG_IS_CLEAR(ap->ap_Flags,APF_DIDDIR)) { SET_FLAG(ap->ap_Flags,APF_DODIR); } else { CLEAR_FLAG(ap->ap_Flags,APF_DIDDIR); check = FALSE; } } if(check) { SHOWSTRING(ap->ap_Info.fib_FileName); if(do_match(ap->ap_Info.fib_FileName,whichPattern)) { D(("Output |%s/%s|",originalDrawerName,ap->ap_Buf)); if(fprintf(out,"%s/%s\n",originalDrawerName,ap->ap_Buf) < 0) { stopped = TRUE; break; } } else { SHOWMSG("but its name does not match"); } } } while((error = MatchNext(ap)) == OK); } MatchEnd(ap); CurrentDir(oldDir); if(NOT stopped) { if(error == ERROR_NO_MORE_ENTRIES) { result = OK; } else if(error == ERROR_BREAK) { /* Don't do anything; we are going * to delete the file anyway. */ } else { SetIoErr(error); MapIoErrToErrno(); } } free(ap); } else { errno = ENOMEM; } } else { MapIoErrToErrno(); } UnLock(dirLock); } else { MapIoErrToErrno(); } UnmangleName(&drawerName,&mi); } return(result); } int amiga_system(char *cmd) { char redirectionFileBuffer[MAX_FILENAME_LEN]; char *redirectionFile = redirectionFileBuffer; struct MangleInfo mi; int result = ERROR; char *stop; char *options; char *cmdName; int cmdLen,i; int quoteCount; chkabort(); ASSERT(cmd != NULL); ENTER(); SHOWSTRING(cmd); /* Handle two special cases for the smb client program, * which for reasons unknown to me resorts to calling * the Unix "ls" and "find" programs to collect lists * of files. * * The commands to watch for take the following forms: * * /bin/ls file_to_list > output_filename * find directory_to_list -name "pattern_to_list" -print > output_filename */ stop = cmd; while((*stop) != ' ' && (*stop) != '\0') stop++; /* This will point behind the command name, * to the beginning of the list of options, * if any. */ options = stop; while((*options) == ' ') options++; /* Now go and isolate the command name. */ cmdName = cmd; cmdLen = stop-cmd; for(i = cmdLen - 1 ; i >= 0 ; i--) { if(cmd[i] == '/' || cmd[i] == ':') { cmdName = &cmd[i+1]; stop = cmdName; while((*stop) != ' ' && (*stop) != '\0') stop++; cmdLen = stop-cmdName; break; } } /* We have isolated the command name, now * find the redirection file name. */ quoteCount = 0; strcpy(redirectionFile,""); for(i = 0 ; i < strlen(cmd) ; i++) { if(cmd[i] == '\"') quoteCount = (1-quoteCount); if(cmd[i] == '>' && quoteCount == 0) { char * from; from = &cmd[i+1]; while((*from) == ' ') from++; /* Note that we rely upon the * redirection file name not to contain * quote or escape characters. */ nstrcpy_blank(sizeof(redirectionFileBuffer),redirectionFile,from); break; } } /* Now check which command we got. */ if(cmdLen == 2 && Strnicmp(cmdName,"ls",2) == SAME) { char localDirName[MAX_FILENAME_LEN]; char * dirName; dirName = "."; if(TranslateRelativePath(&dirName,localDirName,sizeof(localDirName)) == OK) { char whichPatternBuffer[MAX_FILENAME_LEN]; char *whichPattern = whichPatternBuffer; BOOL oldAllowBreak = AllowBreak; /* We don't want to be interrupted. */ AllowBreak = FALSE; /* Now find the pattern to use. */ nstrcpy_blank(sizeof(whichPatternBuffer),whichPattern,options); SHOWMSG("doing `ls'"); SHOWSTRING(whichPattern); SHOWSTRING(redirectionFile); /* What happens next is that the contents of the * current directory are scanned. All names * that match the given pattern are stored in the * redirection file. */ if(MangleName(&redirectionFile,&mi) == OK) { FILE * out; ForbidDOS(); out = fopen(redirectionFile,"wb"); if(out != NULL) { SHOWSTRING(localDirName); if(strcmp(localDirName,"/") == SAME) { struct List list; struct Node * node; SHOWMSG("listing volumes"); NewList(&list); fill_volume_list(&list); while((node = RemHead(&list)) != NULL) { D(("checking '%s'",node->ln_Name)); if(do_match(node->ln_Name,whichPattern)) { SHOWMSG(">>> matches"); if(fprintf(out,"%s\n",node->ln_Name) < 0) break; } else { SHOWMSG(">>> does not match"); } free(node); } while((node = RemHead(&list)) != NULL) free(node); } else { BPTR dirLock; SHOWMSG("listing drawers"); dirLock = Lock("",SHARED_LOCK); if(dirLock != ZERO) { D_S(struct FileInfoBlock,fib); if(Examine(dirLock,fib)) { BOOL stopped = FALSE; while(ExNext(dirLock,fib)) { D(("checking '%s'",fib->fib_FileName)); if(do_match(fib->fib_FileName,whichPattern)) { SHOWMSG(">>> matches"); if(fprintf(out,"%s\n",fib->fib_FileName) < 0) { stopped = TRUE; break; } } else { SHOWMSG(">>> does not match"); } } if(NOT stopped) { LONG error = IoErr(); if(error == ERROR_NO_MORE_ENTRIES) { result = OK; } else { SetIoErr(error); MapIoErrToErrno(); } } } else { MapIoErrToErrno(); } UnLock(dirLock); } else { MapIoErrToErrno(); } } fclose(out); if(result != OK) DeleteFile(redirectionFile); } PermitDOS(); UnmangleName(&cmd,&mi); } AllowBreak = oldAllowBreak; } } else if (cmdLen == 4 && Strnicmp(cmdName,"find",4) == SAME) { char whichDrawerBuffer[MAX_FILENAME_LEN]; char * whichDrawer = whichDrawerBuffer; char whichPattern[MAX_FILENAME_LEN]; const int _nameLen = strlen("-name"); BOOL oldAllowBreak = AllowBreak; struct Node * node; /* We don't want to be interrupted. */ AllowBreak = FALSE; /* Now find the drawer to examine. */ nstrcpy_blank(sizeof(whichDrawerBuffer),whichDrawer,options); /* Find the pattern to search for. */ strcpy(whichPattern,""); for(i = 0 ; i < strlen(options) ; i++) { if(Strnicmp(&options[i],"-name",_nameLen) == SAME) { const int maxLen = sizeof(whichPattern)-1; int whichPatternLen; int escapeCount; int quoteCount; char * from; char * to; /* `from' should now point straight at the * search pattern, which is probably enclosed * in quote characters. */ from = &options[i+_nameLen]; while((*from) == ' ') from++; to = whichPattern; whichPatternLen = 0; quoteCount = 0; escapeCount = 0; while((*from) != '\0' && whichPatternLen < maxLen) { if(escapeCount != 0) { (*to++) = (*from++); whichPatternLen++; escapeCount = 0; } else if((*from) == '\\') { escapeCount = (1-escapeCount); from++; } else if ((*from) == '\"') { quoteCount = (1-quoteCount); from++; } else { if((*from) == ' ' && quoteCount == 0) { break; } else { (*to++) = (*from++); whichPatternLen++; } } } (*to) = '\0'; } } SHOWMSG("doing `find'"); SHOWSTRING(whichDrawer); SHOWSTRING(whichPattern); SHOWSTRING(redirectionFile); /* Things are a little bit more complicated here. The * "find" program scans recursively through a directory * tree. We emulate this behaviour by having the MatchFirst/MatchNext * routines iterate through the directory tree. The * name of every file found is matched against the given * pattern and, if necessary, written to the redirection * file. Note that matching is performed only on the * file name, not on the entire path. */ if(MangleName(&redirectionFile,&mi) == OK) { FILE * out; ForbidDOS(); out = fopen(redirectionFile,"wb"); if(out != NULL) { char localDirName[MAX_FILENAME_LEN]; char * dirName; dirName = whichDrawer; if(TranslateRelativePath(&dirName,localDirName,sizeof(localDirName)) == OK) { D(("listing path '%s'",dirName)); if(strcmp(dirName,"/") == SAME) { struct List list; SHOWMSG("checking all 'drawers' in the fake root"); NewList(&list); fill_volume_list(&list); while((node = RemHead(&list)) != NULL) { strcpy(localDirName,"/"); strcat(localDirName,node->ln_Name); free(node); D(("checking '%s'",localDirName)); result = deep_scan_drawer(localDirName,whichPattern,out); if(result != OK) break; } while((node = RemHead(&list)) != NULL) free(node); } else { SHOWMSG("checking only a local drawer"); result = deep_scan_drawer(dirName,whichPattern,out); } } fclose(out); if(result != OK) DeleteFile(redirectionFile); } PermitDOS(); UnmangleName(&cmd,&mi); } AllowBreak = oldAllowBreak; } else { if(MangleName(&cmd,&mi) == OK) { ForbidDOS(); result = system(cmd); PermitDOS(); UnmangleName(&cmd,&mi); } } RETURN(result); return(result); } /******************************************************************************/ int amiga_fork(VOID) /* dummy */ { int result; chkabort(); ENTER(); result = ERROR; errno = ENOSYS; RETURN(result); return(result); } /******************************************************************************/ STATIC BOOL SetFileSocket(FILE *stream,int sockfd) { struct UFB * ufb; BOOL success = FALSE; ASSERT(stream != NULL); ENTER(); /* Check which buffer the file * is attached to. */ ufb = chkufb(fileno(stream)); if(ufb != NULL) { SHOWVALUE(ufb); /* Save the file handle. */ if(SaveDescriptor(ufb)) { /* And put the socket in its place. */ ufb->ufbfh = sockfd; SET_FLAG(ufb->ufbflg,UFB_IS_SOCKET); success = TRUE; } } else { SHOWMSG("no ufb"); } RETURN(success); return(success); } STATIC VOID DaemonInit(VOID) { struct DaemonMessage * dm; int sock; ENTER(); /* This routine is called when the program starts up. * If it was launched by the INet super server, this * program will run as a daemon. We pick up its * socket input stream and attach it to our stdio * streams. */ dm = (struct DaemonMessage *)((struct Process *)FindTask(NULL))->pr_ExitData; if(dm != NULL) { sock = ObtainSocket(dm->dm_ID,dm->dm_Family,dm->dm_Type,0); if(sock != -1) { BOOL success = FALSE; int sock_stdin; int sock_stdout; int sock_stderr; sock_stdin = Dup2Socket(sock,-1); sock_stdout = Dup2Socket(sock,-1); sock_stderr = Dup2Socket(sock,-1); if(sock_stdin != -1 && sock_stdout != -1 && sock_stderr != -1) { SHOWMSG("got all sockets"); D(("stdin = sock %ld",sock_stdin)); D(("stdout = sock %ld",sock_stdout)); D(("stderr = sock %ld",sock_stderr)); if(SetFileSocket(stdin,sock_stdin) && SetFileSocket(stdout,sock_stdout) && SetFileSocket(stderr,sock_stderr)) { SHOWMSG("and all streams are initialized"); success = TRUE; } else { SHOWMSG("failed to initialize streams"); } } else { SHOWMSG("didn't get the sockets"); } if(NO success) { if(sock_stdin != -1) CloseSocket(sock_stdin); if(sock_stdout != -1) CloseSocket(sock_stdout); if(sock_stderr != -1) CloseSocket(sock_stderr); } } else { SHOWMSG("no socket"); } } else { SHOWMSG("No daemon message"); } LEAVE(); } /******************************************************************************/ VOID __tzset(VOID) { STATIC char TimeZoneName[20] = ""; /* This routine sets up the internal * time zone variable according to * the local settings. */ if(STRING_IS_EMPTY(TimeZoneName)) { int hoursWest = MinutesWest / 60; if(hoursWest >= 0) sprintf(TimeZoneName,"GMT+%02d", hoursWest); else sprintf(TimeZoneName,"GMT-%02d",-hoursWest); } _TZ = TimeZoneName; } /******************************************************************************/ time_t time(time_t *timeptr) { time_t currentTime; struct timeval tv; chkabort(); GetSysTime((APTR)&tv); currentTime = UNIX_TIME_OFFSET + tv.tv_secs + 60*MinutesWest; /* translate from local time to UTC */ if(timeptr != NULL) (*timeptr) = currentTime; return(currentTime); } /******************************************************************************/ STATIC struct tm * ConvertTime(ULONG seconds) { STATIC struct tm tm; struct ClockData clockData; ULONG delta; chkabort(); Amiga2Date(seconds,&clockData); tm.tm_sec = clockData.sec; tm.tm_min = clockData.min; tm.tm_hour = clockData.hour; tm.tm_mday = clockData.mday; tm.tm_mon = clockData.month - 1; tm.tm_year = clockData.year - 1900; tm.tm_wday = clockData.wday; tm.tm_isdst = 0; clockData.mday = 1; clockData.month = 1; delta = Date2Amiga(&clockData); tm.tm_yday = (seconds - delta) / (24*60*60); return(&tm); } struct tm * gmtime(const time_t *t) { struct tm * result; ULONG seconds; if((*t) < UNIX_TIME_OFFSET) seconds = 0; else seconds = (*t) - UNIX_TIME_OFFSET; result = ConvertTime(seconds); return(result); } struct tm * localtime(const time_t *t) { struct tm * result; ULONG seconds; if((*t) < (UNIX_TIME_OFFSET + 60*MinutesWest)) seconds = 0; else seconds = (*t) - (UNIX_TIME_OFFSET + 60*MinutesWest); /* translate from UTC to local time */ result = ConvertTime(seconds); return(result); } /******************************************************************************/ int amiga_strcasecmp(char *a,char *b) { int result; ASSERT(a != NULL && b != NULL); result = Stricmp((STRPTR)a,b); return(result); } int amiga_strncasecmp(char *a,char *b,int len) { int result; ASSERT(a != NULL && b != NULL); result = Strnicmp(a,b,len); return(result); } /******************************************************************************/ VOID (*amiga_signal(int which,VOID (* action)(int)))(int) { VOID (* result)(int); chkabort(); ENTER(); if(SIGABRT <= which && which <= SIGTERM) result = signal(which,action); else result = SIG_DFL; RETURN(result); return(result); } /******************************************************************************/ VOID amiga_alarm(int seconds) /* dummy */ { chkabort(); ENTER(); LEAVE(); } /******************************************************************************/ int amiga_waitpid(pid_t pid,int *status,int options) /* dummy */ { int result; chkabort(); ENTER(); result = ERROR; errno = ENOSYS; RETURN(result); return(result); } /******************************************************************************/ long amiga_setsid(VOID) { long result; chkabort(); ENTER(); result = (long)setsid(); RETURN(result); return(result); } /******************************************************************************/ int amiga_setreuid(uid_t real, uid_t eff) { int result; chkabort(); ENTER(); result = setreuid(real,eff); RETURN(result); return(result); } /******************************************************************************/ int amiga_setregid(gid_t real, gid_t eff) { int result; chkabort(); ENTER(); result = setregid(real,eff); RETURN(result); return(result); } /******************************************************************************/ int amiga_getsockname(int sockfd,struct sockaddr *name,int *namelen) { struct UFB * ufb; int result = ERROR; chkabort(); ASSERT(name != NULL && namelen != NULL); ENTER(); ufb = chkufb(sockfd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = getsockname(ufb->ufbfh,name,(LONG *)namelen); else errno = ENOTSOCK; RETURN(result); return(result); } /******************************************************************************/ int amiga_statfs(char *name,struct statfs *f) { char localName[MAX_FILENAME_LEN]; int result = ERROR; chkabort(); ASSERT(name != NULL && f != NULL); ENTER(); SHOWSTRING(name); SHOWVALUE(f); if(TranslateRelativePath(&name,localName,sizeof(localName)) == OK) { /* Make up something for the virtual root directory. */ if(strcmp(name,"/") == SAME) { memset(f,0,sizeof(*f)); f->f_fsize = 512; /* Try to reuse the data from our last scan. */ if(RootBlocks > 0 && RootBlocksUsed > 0) { f->f_blocks = RootBlocks; f->f_bfree = RootBlocks - RootBlocksUsed; } else { f->f_blocks = 0x7FFFFFFF/f->f_fsize; f->f_bfree = f->f_blocks - 1; } f->f_bsize = f->f_fsize; f->f_bavail = f->f_bfree; SHOWVALUE(f->f_fsize); SHOWVALUE(f->f_bsize); SHOWVALUE(f->f_blocks); SHOWVALUE(f->f_bfree); SHOWVALUE(f->f_bavail); result = OK; } else { struct MangleInfo mi; BPTR fileLock; if(MangleName(&name,&mi) == OK) { ForbidDOS(); fileLock = Lock(name,SHARED_LOCK); if(fileLock != ZERO) { D_S(struct InfoData,id); if(Info(fileLock,id)) { memset(f,0,sizeof(*f)); /* Make sure that these never drop to zero. */ if(id->id_NumBlocks == 0) id->id_NumBlocks = 1; if(id->id_BytesPerBlock == 0) id->id_BytesPerBlock = 512; f->f_fsize = id->id_BytesPerBlock; /* fundamental file system block size */ f->f_blocks = id->id_NumBlocks; /* total data blocks in file system */ f->f_bfree = id->id_NumBlocks - id->id_NumBlocksUsed; /* free blocks in fs */ f->f_bsize = f->f_fsize; /* optimal transfer block size */ f->f_bavail = f->f_bfree; /* free blocks avail to non-superuser */ SHOWVALUE(f->f_fsize); SHOWVALUE(f->f_bsize); SHOWVALUE(f->f_blocks); SHOWVALUE(f->f_bfree); SHOWVALUE(f->f_bavail); result = OK; } else { MapIoErrToErrno(); } UnLock(fileLock); } else { MapIoErrToErrno(); } PermitDOS(); UnmangleName(&name,&mi); } } } RETURN(result); return(result); } /******************************************************************************/ int amiga_execl(char *path,char *arg,...) /* dummy */ { int result; chkabort(); ENTER(); result = ERROR; errno = ENOSYS; RETURN(result); return(result); } /******************************************************************************/ char * amiga_strerror(int error) { struct TagItem tags[2]; char *result; chkabort(); ENTER(); tags[0].ti_Tag = SBTM_GETVAL(SBTC_ERRNOSTRPTR); tags[0].ti_Data = error; tags[1].ti_Tag = TAG_END; SocketBaseTagList(tags); result = (char *)tags[0].ti_Data; RETURN(result); return(result); } /******************************************************************************/ int amiga_access(char *name,int modes) { struct MangleInfo mi; int result = ERROR; chkabort(); ASSERT(name != NULL); ENTER(); if(MangleName(&name,&mi) == OK) { ForbidDOS(); result = access(name,modes); PermitDOS(); UnmangleName(&name,&mi); } RETURN(result); return(result); } /******************************************************************************/ STATIC VOID MapIoErrToErrno(VOID) { /* This routine maps AmigaDOS error codes to * Unix error codes, as far as this is possible. * This table contains AmigaDOS error codes * the emulated routines won't generate. I have * included them for the sake of completeness. */ struct { LONG IoErr; LONG errno; } MapTable[] = { ERROR_NO_FREE_STORE, ENOMEM, ERROR_TASK_TABLE_FULL, ENOMEM, ERROR_BAD_TEMPLATE, EINVAL, ERROR_BAD_NUMBER, EINVAL, ERROR_REQUIRED_ARG_MISSING, EINVAL, ERROR_KEY_NEEDS_ARG, EINVAL, ERROR_TOO_MANY_ARGS, EINVAL, ERROR_UNMATCHED_QUOTES, EINVAL, ERROR_LINE_TOO_LONG, ENAMETOOLONG, ERROR_FILE_NOT_OBJECT, ENOEXEC, ERROR_INVALID_RESIDENT_LIBRARY, EIO, ERROR_NO_DEFAULT_DIR, EIO, ERROR_OBJECT_IN_USE, EBUSY, ERROR_OBJECT_EXISTS, EBUSY, ERROR_DIR_NOT_FOUND, ENOENT, ERROR_OBJECT_NOT_FOUND, ENOENT, ERROR_BAD_STREAM_NAME, EINVAL, ERROR_OBJECT_TOO_LARGE, EFBIG, ERROR_ACTION_NOT_KNOWN, ENOSYS, ERROR_INVALID_COMPONENT_NAME, EINVAL, ERROR_INVALID_LOCK, EBADF, ERROR_OBJECT_WRONG_TYPE, EFTYPE, ERROR_DISK_NOT_VALIDATED, EROFS, ERROR_DISK_WRITE_PROTECTED, EROFS, ERROR_RENAME_ACROSS_DEVICES, EXDEV, ERROR_DIRECTORY_NOT_EMPTY, ENOTEMPTY, ERROR_TOO_MANY_LEVELS, ENAMETOOLONG, ERROR_DEVICE_NOT_MOUNTED, ENXIO, ERROR_SEEK_ERROR, EIO, ERROR_COMMENT_TOO_BIG, ENAMETOOLONG, ERROR_DISK_FULL, ENOSPC, ERROR_DELETE_PROTECTED, EACCES, ERROR_WRITE_PROTECTED, EACCES, ERROR_READ_PROTECTED, EACCES, ERROR_NOT_A_DOS_DISK, EFTYPE, ERROR_NO_DISK, EACCES, ERROR_NO_MORE_ENTRIES, EIO, ERROR_IS_SOFT_LINK, EFTYPE, ERROR_OBJECT_LINKED, EIO, ERROR_BAD_HUNK, ENOEXEC, ERROR_NOT_IMPLEMENTED, ENOSYS, ERROR_RECORD_NOT_LOCKED, EIO, ERROR_LOCK_COLLISION, EACCES, ERROR_LOCK_TIMEOUT, EIO, ERROR_UNLOCK_ERROR, EIO, ERROR_BUFFER_OVERFLOW, EIO, ERROR_BREAK, EINTR, ERROR_NOT_EXECUTABLE, ENOEXEC }; LONG ioErr = IoErr(); int i; /* If nothing else matches, we can always * flag it as an I/O error. */ errno = EIO; for(i = 0 ; i < NUM_ENTRIES(MapTable) ; i++) { if(MapTable[i].IoErr == ioErr) { errno = MapTable[i].errno; break; } } } /******************************************************************************/ off_t amiga_lseek(int fd,off_t offset,int mode) { struct UFB * ufb; off_t result = ERROR; ENTER(); SHOWVALUE(fd); SHOWVALUE(offset); SHOWVALUE(mode); ForbidDOS(); /* Make sure that we operate on a file. */ ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_CLEAR(ufb->ufbflg,UFB_IS_SOCKET)) result = lseek(fd,offset,mode); else errno = EBADF; PermitDOS(); RETURN(result); return(result); } /******************************************************************************/ int amiga_chroot(char *name) /* dummy */ { int result = OK; ENTER(); SHOWSTRING(name); RETURN(result); return(result); } /******************************************************************************/ STATIC int MapFileNameAmigaToUnix( const char * amiga, char * unix, int maxUnixLen) { int len,destLen; int result = ERROR; /* This routine makes a Unix file * name generated from an AmigaDOS * file name. This involves a slight * change of syntax... */ D(("amiga name |%s|",amiga)); len = destLen = strlen(amiga); if(amiga[0] == '/') { destLen = 2 + len; } else { int i; for(i = 0 ; i < len ; i++) { if(amiga[i] == ':') { if(amiga[i+1] != '\0') destLen = 1 + len; break; } } } if(destLen < maxUnixLen) { if(amiga[0] == '/') { /* `/foo\0' -> `../foo\0' */ memmove(&unix[2],amiga,len+1); strncpy(unix,"..",2); } else { BOOL done = FALSE; int i; for(i = 0 ; i < len ; i++) { if(amiga[i] == ':') { if(amiga[i+1] == '\0') { /* `foo:\0' -> `/foo\0' */ memmove(&unix[1],amiga,i); unix[0] = '/'; unix[i+1] = '\0'; } else { /* `foo:bar\0' -> `/foo/bar\0' */ memmove(&unix[1],amiga,len+1); unix[0] = '/'; unix[i+1] = '/'; } done = TRUE; break; } } if(NOT done && unix != amiga) strcpy(unix,amiga); } D(("unix name |%s|",unix)); result = OK; } else { SHOWMSG("unix name is too long to fit"); errno = ENAMETOOLONG; } return(result); } /******************************************************************************/ STATIC VOID FlushSTDOUT(VOID) { /* Flush the standard output streams so that * any following output will be printed after * any buffered stdio output. */ if(WBenchMsg == NULL) { struct UFB * ufb; /* Don't let anybody stop us. */ signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN); ufb = chkufb(fileno(stdout)); if(ufb != NULL && FLAG_IS_CLEAR(ufb->ufbflg,UFB_IS_SOCKET)) fflush(stdout); ufb = chkufb(fileno(stderr)); if(ufb != NULL && FLAG_IS_CLEAR(ufb->ufbflg,UFB_IS_SOCKET)) fflush(stderr); } } VOID _CXOVF(VOID) { /* This routine is called when a stack overflow occurs. */ FlushSTDOUT(); ReportProblem("Stack overflow"); exit(RETURN_ERROR); } VOID __regargs _CXBRK(VOID) { extern STRPTR _ProgramName; FlushSTDOUT(); /* This routine is called when the program is interrupted. */ if(DOSBase->lib_Version >= 37) { PrintFault(ERROR_BREAK,_ProgramName); } else { const char *famousLastWords = ": *** Break"; BPTR output = Output(); Write(output,(APTR)famousLastWords,strlen(famousLastWords)); Write(output,_ProgramName,strlen(_ProgramName)); Write(output,"\n",1); } exit(RETURN_WARN); } /******************************************************************************/ STATIC STRPTR SambaSemaphoreName = "Amiga Samba"; struct SambaClientNode { struct MinNode scn_MinNode; struct Task * scn_Task; pid_t scn_PID; }; struct SambaSemaphore { struct SignalSemaphore ss_Semaphore; struct List ss_ClientList; pid_t ss_PID; struct List ss_FileLockList; }; STATIC struct SambaSemaphore * SambaSemaphore; STATIC struct SambaClientNode * ThisClient; STATIC VOID CleanupSambaSemaphore(VOID) { if(ThisClient != NULL) { /* Unregister the program. */ ObtainSemaphore((struct SignalSemaphore *)SambaSemaphore); Remove((struct Node *)ThisClient); ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); FreeVec(ThisClient); ThisClient = NULL; } } STATIC BOOL SetupSambaSemaphore(VOID) { BOOL success = FALSE; /* We have to have the timer open to serialize the process IDs properly. */ if(TimerBase != NULL) { Forbid(); /* Try to find the global rendezvous semaphore. */ SambaSemaphore = (struct SambaSemaphore *)FindSemaphore(SambaSemaphoreName); if(SambaSemaphore == NULL) { /* Couldn't find it. So, we create it... */ SambaSemaphore = AllocMem(sizeof(*SambaSemaphore) + strlen(SambaSemaphoreName)+1,MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR); if(SambaSemaphore != NULL) { /* Set up the name. */ SambaSemaphore->ss_Semaphore.ss_Link.ln_Name = (char *)(SambaSemaphore+1); strcpy(SambaSemaphore->ss_Semaphore.ss_Link.ln_Name,SambaSemaphoreName); /* Set the base value for all process IDs. */ SambaSemaphore->ss_PID = 936; /* Clear the client list tasks are going to be registered with. */ NewList(&SambaSemaphore->ss_ClientList); /* Clear the list we will use for keeping * track of file segment locks. */ NewList(&SambaSemaphore->ss_FileLockList); AddSemaphore((struct SignalSemaphore *)SambaSemaphore); } } Permit(); } /* If possible, register this task. */ if(SambaSemaphore != NULL) { struct SambaClientNode * scn; scn = AllocVec(sizeof(*scn),MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR); if(scn != NULL) { LONG max_cli,i; /* That's me. */ scn->scn_Task = FindTask(NULL); /* Now for the interesting part: assign a PID to this * process which matches its CLI task number. */ Forbid(); max_cli = MaxCli(); for(i = 1 ; i <= max_cli ; i++) { if(FindCliProc(i) == (struct Process *)scn->scn_Task) { scn->scn_PID = i; break; } } Permit(); ObtainSemaphore((struct SignalSemaphore *)SambaSemaphore); AddTail(&SambaSemaphore->ss_ClientList,(struct Node *)scn); ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); ThisClient = scn; success = TRUE; } } return(success); } int amiga_kill(pid_t pid,int sig) { struct SambaClientNode * scn; struct Task * found = NULL; int result = ERROR; chkabort(); ENTER(); ObtainSemaphore((struct SignalSemaphore *)SambaSemaphore); /* Try to find the Samba program that responds * to the given process ID. */ for(scn = (struct SambaClientNode *)SambaSemaphore->ss_ClientList.lh_Head ; scn->scn_MinNode.mln_Succ != NULL ; scn = (struct SambaClientNode *)scn->scn_MinNode.mln_Succ) { if(scn->scn_PID == pid) { found = scn->scn_Task; break; } } /* Did we find one? */ if(found != NULL) { /* Kill the task or just do nothing? */ if(sig == SIGTERM) Signal(found,SIGBREAKF_CTRL_C); result = OK; } else { errno = ESRCH; } ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); RETURN(result); return(result); } pid_t amiga_getpid(VOID) { pid_t result; chkabort(); ENTER(); /* Return this program's process ID. */ result = ThisClient->scn_PID; RETURN(result); return(result); } /******************************************************************************/ struct LockedRegionNode { struct MinNode lrn_MinNode; LONG lrn_Start; LONG lrn_Stop; pid_t lrn_Owner; BOOL lrn_Shared; }; struct FileLockNode { struct MinNode fln_MinNode; struct List fln_LockedRegionList; BPTR fln_FileParentDir; UBYTE fln_FileName[1]; }; STATIC VOID RemoveLockedRegionNode(struct UFB * ufb,LONG start,LONG stop) { if(FLAG_IS_SET(ufb->ufbflg,UFB_LOCKED)) { BPTR fileHandle = (BPTR)ufb->ufbfh; struct FileLockNode * whichLock; /* Find the locked file this descriptor * buffer belongs to. */ if(FindFileLockNodeByFileHandle(fileHandle,&whichLock) == OK && whichLock != NULL) { struct LockedRegionNode * lrn; /* Find the region to unlock and remove it. */ for(lrn = (struct LockedRegionNode *)whichLock->fln_LockedRegionList.lh_Head ; lrn->lrn_MinNode.mln_Succ != NULL ; lrn = (struct LockedRegionNode *)lrn->lrn_MinNode.mln_Succ) { if(lrn->lrn_Owner == ThisClient->scn_PID && lrn->lrn_Start == start && lrn->lrn_Stop == stop) { SHOWMSG("unlocking all regions on this file"); Remove((struct Node *)lrn); DeleteLockedRegionNode(lrn); break; } } /* Check if there are any locked regions left. * If not, mark the entire file as unlocked. */ if(IsListEmpty(&whichLock->fln_LockedRegionList)) { SHOWMSG("no more regions are locked; removing the file lock node"); Remove((struct Node *)whichLock); DeleteFileLockNode(whichLock); CLEAR_FLAG(ufb->ufbflg,UFB_LOCKED); } } } } STATIC VOID DeleteLockedRegionNode(struct LockedRegionNode * lrn) { if(lrn != NULL) FreeMem(lrn,sizeof(*lrn)); } STATIC LONG CreateLockedRegionNode(struct LockedRegionNode ** resultPtr) { struct LockedRegionNode * lrn; LONG error; lrn = AllocMem(sizeof(*lrn),MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR); if(lrn != NULL) { lrn->lrn_Owner = ThisClient->scn_PID; error = OK; } else { error = ERROR_NO_FREE_STORE; } (*resultPtr) = lrn; return(error); } STATIC VOID DeleteFileLockNode(struct FileLockNode * fln) { if(fln != NULL) { UnLock(fln->fln_FileParentDir); FreeVec(fln); } } STATIC LONG CreateFileLockNode(struct UFB * ufb,struct FileLockNode ** resultPtr) { struct FileLockNode * result = NULL; BPTR fileHandle = (BPTR)ufb->ufbfh; D_S(struct FileInfoBlock,fib); LONG error = OK; /* We store a lock on the file's parent directory * and the name of the file for later use in * comparisons. */ if(ExamineFH(fileHandle,fib)) { struct FileLockNode * fln; fln = AllocVec(sizeof(*fln) + strlen(fib->fib_FileName),MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR); if(fln != NULL) { fln->fln_FileParentDir = ParentOfFH(fileHandle); if(fln->fln_FileParentDir != ZERO) { strcpy(fln->fln_FileName,fib->fib_FileName); NewList(&fln->fln_LockedRegionList); result = fln; fln = NULL; } else { error = IoErr(); } DeleteFileLockNode(fln); } else { error = ERROR_NO_FREE_STORE; } } else { error = IoErr(); } (*resultPtr) = result; return(error); } STATIC LONG FindFileLockNodeByFileHandle(BPTR fileHandle,struct FileLockNode ** resultPtr) { struct FileLockNode * result = NULL; BPTR parentDir; LONG error; /* Determine the file's parent directory and * name. These will be compared against the * global file lock data. */ parentDir = ParentOfFH(fileHandle); if(parentDir != ZERO) { D_S(struct FileInfoBlock,this_fib); if(ExamineFH(fileHandle,this_fib)) { struct FileLockNode * fln; #if DEBUG { char name[MAX_FILENAME_LEN]; if(NameFromFH(fileHandle,name,sizeof(name))) D(("Looking for a lock on file |%s|",name)); } #endif /* DEBUG */ error = OK; for(fln = (struct FileLockNode *)SambaSemaphore->ss_FileLockList.lh_Head ; fln->fln_MinNode.mln_Succ != NULL ; fln = (struct FileLockNode *)fln->fln_MinNode.mln_Succ) { /* To be identical, the files must reside in the * same directories and bear the same names. */ if(SameLock(fln->fln_FileParentDir,parentDir) == LOCK_SAME) { if(Stricmp(fln->fln_FileName,this_fib->fib_FileName) == SAME) { result = fln; error = OK; break; } } } } else { error = IoErr(); } if(result != NULL) SHOWMSG("found one"); else SHOWMSG("didn't find one"); UnLock(parentDir); } else { error = IoErr(); } (*resultPtr) = result; return(error); } STATIC VOID FindFileLockNodeByDrawerAndName(BPTR dirLock,STRPTR fileName,struct FileLockNode ** resultPtr) { struct FileLockNode * result = NULL; struct FileLockNode * fln; /* This is a somewhat simplied version of FindFileLockNodeByFileHandle() * which works with preset drawer and name data. */ #if DEBUG { char name[MAX_FILENAME_LEN]; if(NameFromLock(dirLock,name,sizeof(name))) { if(AddPart(name,fileName,sizeof(name))) D(("Looking for a lock on file |%s|",name)); } } #endif /* DEBUG */ for(fln = (struct FileLockNode *)SambaSemaphore->ss_FileLockList.lh_Head ; fln->fln_MinNode.mln_Succ != NULL ; fln = (struct FileLockNode *)fln->fln_MinNode.mln_Succ) { if(SameLock(fln->fln_FileParentDir,dirLock) == LOCK_SAME) { if(Stricmp(fln->fln_FileName,fileName) == SAME) { result = fln; break; } } } if(result != NULL) SHOWMSG("found one"); else SHOWMSG("didn't find one"); (*resultPtr) = result; } STATIC struct LockedRegionNode * FindCollidingRegion(struct FileLockNode * fln,LONG start,LONG stop,BOOL shared) { struct LockedRegionNode * result = NULL; struct LockedRegionNode * lrn; /* This routine looks for a locked region that overlaps * with the specified region. It returns a pointer to the * region that would collide with the specified region if * a new lock were to be added. */ for(lrn = (struct LockedRegionNode *)fln->fln_LockedRegionList.lh_Head ; lrn->lrn_MinNode.mln_Succ != NULL ; lrn = (struct LockedRegionNode *)lrn->lrn_MinNode.mln_Succ) { /* Do the regions overlap? */ if(lrn->lrn_Start <= stop && start <= lrn->lrn_Stop) { /* Two shared regions may always overlap. * How about the rest? */ if(NOT shared || NOT lrn->lrn_Shared) { /* The lock owner may add as many exclusive * or shared locks to the same region as * necessary. */ if(lrn->lrn_Owner == ThisClient->scn_PID) continue; /* So we found a region that would * cause a collision. */ result = lrn; break; } } } return(result); } STATIC VOID CleanupFileLocks(int fd) { struct UFB * ufb; /* This routine removes all locked regions from a file * before it is eventually closed. */ ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_LOCKED)) { BPTR fileHandle = (BPTR)ufb->ufbfh; struct FileLockNode * whichLock; ObtainSemaphore((struct SignalSemaphore *)SambaSemaphore); if(FindFileLockNodeByFileHandle(fileHandle,&whichLock) == OK && whichLock != NULL) { struct LockedRegionNode * lrn_this; struct LockedRegionNode * lrn_next; SHOWMSG("unlocking all regions on this file"); for(lrn_this = (struct LockedRegionNode *)whichLock->fln_LockedRegionList.lh_Head ; (lrn_next = (struct LockedRegionNode *)lrn_this->lrn_MinNode.mln_Succ) != NULL ; lrn_this = lrn_next) { if(lrn_this->lrn_Owner == ThisClient->scn_PID) { Remove((struct Node *)lrn_this); DeleteLockedRegionNode(lrn_this); } } if(IsListEmpty(&whichLock->fln_LockedRegionList)) { SHOWMSG("no more regions are locked; removing the file lock node"); Remove((struct Node *)whichLock); DeleteFileLockNode(whichLock); } } CLEAR_FLAG(ufb->ufbflg,UFB_LOCKED); ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); } } #define SEEK_ERROR (-1) STATIC int HandleFileLocking(int cmd,struct flock * l,struct UFB * ufb) { int result = ERROR; /* This routine implements advisory file segment locking * similar to 4.4BSD, but not quite the same. The functionality * is a subset, somewhat similar to the functionality offered * by the AmigaDOS LockRecord() and UnlockRecord() functions. * This means for example that every unlock request must * match the size and position of the corresponding locking * request. * * This implementation was chosen because not every Amiga * filing system implements record locking and Samba * absolutely requires this functionality to work. */ if(l != NULL && ufb != NULL) { /* Can we make sense of the input parameters? */ if(F_RDLCK <= l->l_type && l->l_type <= F_WRLCK && (l->l_whence == SEEK_SET || l->l_whence == SEEK_CUR || l->l_whence == SEEK_END)) { struct LockedRegionNode * lrn = NULL; struct FileLockNode * fln = NULL; LONG error; if((cmd == F_SETLK || cmd == F_SETLKW) && (l->l_type != F_UNLCK)) { SHOWMSG("this is a lock request"); error = CreateFileLockNode(ufb,&fln); if(error == OK) error = CreateLockedRegionNode(&lrn); } else { SHOWMSG("this is not a lock request"); error = OK; } if(error == OK) { D_S(struct FileInfoBlock,fib); BPTR fileHandle = (BPTR)ufb->ufbfh; BOOL dataIsValid = TRUE; LONG currentPosition; LONG start = 0; LONG len = 0; /* Now calculate the position of the * first byte to lock and the number * of bytes to lock. */ switch(l->l_whence) { case SEEK_SET: start = l->l_start; if(l->l_len == 0) { if(ExamineFH(fileHandle,fib)) len = fib->fib_Size - start; else dataIsValid = FALSE; } else { len = l->l_len; } break; case SEEK_CUR: currentPosition = Seek(fileHandle,0,OFFSET_CURRENT); if(currentPosition != SEEK_ERROR) { start = currentPosition + l->l_start; if(l->l_len == 0) { if(ExamineFH(fileHandle,fib)) len = fib->fib_Size - start; else dataIsValid = FALSE; } else { len = l->l_len; } } else { dataIsValid = FALSE; } break; case SEEK_END: default: if(ExamineFH(fileHandle,fib)) { start = fib->fib_Size + l->l_start; if(l->l_len == 0) len = fib->fib_Size - start; else len = l->l_len; } else { dataIsValid = FALSE; } break; } SHOWVALUE(start); SHOWVALUE(len); /* Did we get everything we needed? */ if(dataIsValid) { if(start >= 0 && len >= 0) { if(len > 0) { if(l->l_type == F_UNLCK) { D(("unlocking %ld..%ld",start,start+len-1)); ObtainSemaphore((struct SignalSemaphore *)SambaSemaphore); RemoveLockedRegionNode(ufb,start,start+len-1); ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); result = OK; } else if(cmd == F_SETLKW) { BPTR parentDir; D((" locking %ld..%ld",start,start+len-1)); parentDir = ParentOfFH(fileHandle); if(parentDir != ZERO) { D_S(struct FileInfoBlock,fib); if(ExamineFH(fileHandle,fib)) { BOOL shared = (BOOL)(l->l_type == F_RDLCK); struct FileLockNode * existing_fln; BOOL stopped = FALSE; BOOL locked; if(shared) D(("this is a shared lock; waiting for completion")); else D(("this is an exclusive lock; waiting for completion")); lrn->lrn_Start = start; lrn->lrn_Stop = start+len-1; lrn->lrn_Shared = shared; /* Retry until we manage to lock the record. */ locked = FALSE; do { ObtainSemaphore((struct SignalSemaphore *)SambaSemaphore); FindFileLockNodeByDrawerAndName(parentDir,fib->fib_FileName,&existing_fln); if(existing_fln != NULL) { SHOWMSG("that file is already locked by someone"); if(FindCollidingRegion(existing_fln,start,start+len-1,shared) == NULL) { SHOWMSG("but the locks don't collide"); AddTail(&existing_fln->fln_LockedRegionList,(struct Node *)lrn); lrn = NULL; locked = TRUE; } else { SHOWMSG("and the locks collide"); } } else { SHOWMSG("nobody has any locks on this file"); AddTail(&SambaSemaphore->ss_FileLockList,(struct Node *)fln); AddTail(&fln->fln_LockedRegionList,(struct Node *)lrn); fln = NULL; lrn = NULL; locked = TRUE; } ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); if(NOT locked) { const int randMax = RAND_MAX / 65536; LONG numRandomTicks; if(CheckAbort()) { stopped = TRUE; break; } /* Wait a little before retrying * the locking operation. We add * a little randomness here to * reduce the likelihood of two * competing processes trying to * lock the same file at the * same time. */ numRandomTicks = ((TICKS_PER_SECOND / 2) * (rand() / 65536)) / randMax; if(numRandomTicks > 0) Delay(numRandomTicks); } } while(NOT locked); if(stopped) { SHOWMSG("lock polling loop stopped"); DeleteFileLockNode(fln); fln = NULL; DeleteLockedRegionNode(lrn); lrn = NULL; UnLock(parentDir); parentDir = ZERO; errno = EINTR; raise(SIGINT); } if(locked) { SHOWMSG("the file now has a lock set"); SET_FLAG(ufb->ufbflg,UFB_LOCKED); result = OK; } } else { SHOWMSG("couldn't read this file's name"); MapIoErrToErrno(); } UnLock(parentDir); } else { SHOWMSG("couldn't get a lock on the file's parent directory"); MapIoErrToErrno(); } } else if(cmd == F_SETLK) { BOOL shared = (BOOL)(l->l_type == F_RDLCK); struct FileLockNode * existing_fln; BOOL locked = FALSE; if(shared) D(("this is a shared lock")); else D(("this is an exclusive lock")); lrn->lrn_Start = start; lrn->lrn_Stop = start+len-1; lrn->lrn_Shared = shared; ObtainSemaphore((struct SignalSemaphore *)SambaSemaphore); if(FindFileLockNodeByFileHandle(fileHandle,&existing_fln) == OK) { if(existing_fln != NULL) { SHOWMSG("that file is already locked by someone else"); if(FindCollidingRegion(existing_fln,start,start+len-1,shared) == NULL) { SHOWMSG("but the locks don't collide"); AddTail(&existing_fln->fln_LockedRegionList,(struct Node *)lrn); lrn = NULL; locked = TRUE; } else { SHOWMSG("and the locks collide"); } } else { SHOWMSG("nobody has any locks on this file"); AddTail(&SambaSemaphore->ss_FileLockList,(struct Node *)fln); AddTail(&fln->fln_LockedRegionList,(struct Node *)lrn); fln = NULL; lrn = NULL; locked = TRUE; } } ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); if(locked) { SHOWMSG("the file now has a lock set"); SET_FLAG(ufb->ufbflg,UFB_LOCKED); result = OK; } else { errno = EACCES; } } else if (cmd == F_GETLK) { struct FileLockNode * fln; LONG error; SafeObtainSemaphoreShared((struct SignalSemaphore *)SambaSemaphore); SHOWMSG("checking for possible lock collision"); error = FindFileLockNodeByFileHandle(fileHandle,&fln); if(error == OK) { if(fln != NULL) { struct LockedRegionNode * lrn; BOOL shared; SHOWMSG("somebody has locked this file"); shared = (BOOL)(l->l_type == F_RDLCK); lrn = FindCollidingRegion(fln,start,start+len-1,shared); if(lrn != NULL) { SHOWMSG("there is a possible lock collision"); l->l_type = (lrn->lrn_Shared ? F_RDLCK : F_WRLCK); l->l_whence = SEEK_SET; l->l_start = lrn->lrn_Start; l->l_len = lrn->lrn_Stop - lrn->lrn_Start + 1; l->l_pid = lrn->lrn_Owner; } else { SHOWMSG("there is no lock collision"); l->l_type = F_UNLCK; } } else { SHOWMSG("nobody has locked this file"); l->l_type = F_UNLCK; } result = OK; } else { SetIoErr(error); MapIoErrToErrno(); } ReleaseSemaphore((struct SignalSemaphore *)SambaSemaphore); } } else { SHOWMSG("zero length lock/unlock"); result = OK; } } else { SHOWMSG("invalid start/len"); SHOWVALUE(start); SHOWVALUE(len); errno = EINVAL; } } else { SHOWMSG("couldn't determine start/len"); MapIoErrToErrno(); } } else { SHOWMSG("couldn't get the bookkeeping memory needed"); MapIoErrToErrno(); } DeleteFileLockNode(fln); DeleteLockedRegionNode(lrn); } else { SHOWMSG("invalid lock type or seek offset"); SHOWVALUE(l->l_type); SHOWVALUE(l->l_whence); errno = EINVAL; } } else { SHOWMSG("no flock or no ufb"); SHOWVALUE(l); SHOWVALUE(ufb); errno = EINVAL; } RETURN(result); return(result); } int amiga_fcntl(int fd,int cmd,unsigned long arg) { struct UFB * ufb; struct flock * l; int result = ERROR; chkabort(); ENTER(); /* Don't try to lock data in a socket... */ ufb = chkufb(fd); if(ufb == NULL || FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) ufb = NULL; l = (struct flock *)arg; switch(cmd) { /* Get the first lock that blocks the lock description pointed to * by the third argument, arg, taken as a pointer to a struct * flock (see above). The information retrieved overwrites the * information passed to fcntl in the flock structure. If no * lock is found that would prevent this lock from being created, * the structure is left unchanged by this function call except * for the lock type which is set to F_UNLCK. */ case F_GETLK: SHOWMSG("F_GETLK"); result = HandleFileLocking(cmd,l,ufb); break; /* Set or clear a file segment lock according to the lock de- * scription pointed to by the third argument, arg, taken as a * pointer to a struct flock (see above). F_SETLK is used to es- * tablish shared (or read) locks (F_RDLCK) or exclusive (or * write) locks, (F_WRLCK), as well as remove either type of lock * (F_UNLCK). If a shared or exclusive lock cannot be set, fcntl * returns immediately with EACCES. */ case F_SETLK: SHOWMSG("F_SETLK"); result = HandleFileLocking(cmd,l,ufb); break; /* This command is the same as F_SETLK except that if a shared or * exclusive lock is blocked by other locks, the process waits * until the request can be satisfied. If a signal that is to be * caught is received while fcntl is waiting for a region, the * fcntl will be interrupted if the signal handler has not speci- * fied the SA_RESTART (see sigaction(2)). */ case F_SETLKW: SHOWMSG("F_SETLKW"); result = HandleFileLocking(cmd,l,ufb); break; /* Get descriptor status flags, as described below (arg is ig- * noted). */ case F_GETFL: SHOWMSG("F_GETFL"); result = IsDescriptorNonblocking(fd) ? O_NONBLOCK : 0; break; /* Set descriptor status flags to arg. */ case F_SETFL: SHOWMSG("F_SETFL"); if(FLAG_IS_SET(arg,O_NONBLOCK)) BlockDescriptor(fd); else UnblockDescriptor(fd); result = OK; break; default: D(("Unknown command %ld",cmd)); errno = ENOSYS; break; } RETURN(result); return(result); } /******************************************************************************/ int amiga_fgetc(FILE *in) { struct UFB * ufb; int result = ERROR; PUSH_ASSERTS(); chkabort(); ufb = chkufb(fileno(in)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { UBYTE c; ENTER(); SHOWMSG("input from socket"); if(recv(ufb->ufbfh,&c,1,0) == 1) result = c; } else { result = fgetc(in); } RETURN(result); POP(); return(result); } char * amiga_fgets(char *str,int n,FILE * in) { struct UFB * ufb; char *result; ENTER(); ufb = chkufb(fileno(in)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { char *s = str; LONG rc; UBYTE c; SHOWMSG("input from socket"); result = str; while(--n > 0) { chkabort(); rc = recv(ufb->ufbfh,&c,1,0); if(rc == 1) { (*str++) = c; if(c == '\n') break; } else { /* End of stream or nothing read? */ if(rc < 0 || str == s) result = NULL; break; } } if(result != NULL) (*str) = '\0'; } else { result = fgets(str,n,in); } RETURN(result); return(result); } int amiga_fputs(const char *str,FILE *out) { struct UFB * ufb; int result; chkabort(); ENTER(); ufb = chkufb(fileno(out)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { SHOWMSG("output to socket"); if(NOT STRING_IS_EMPTY(str)) result = send(ufb->ufbfh,str,strlen(str),0); else result = OK; if(result == OK) result = send(ufb->ufbfh,"\n",1,0); } else { result = fputs(str,out); } RETURN(result); return(result); } int amiga_puts(const char *str) { int result; result = amiga_fputs(str,stdout); return(result); } int amiga_vfprintf(FILE *out,const char *fmt,va_list args) { struct UFB * ufb; int result; chkabort(); ENTER(); ufb = chkufb(fileno(out)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { char buffer[1024]; SHOWMSG("output to socket"); vsnprintf(buffer,sizeof(buffer)-1,fmt,args); buffer[sizeof(buffer)-1] = '\0'; if(NOT STRING_IS_EMPTY(buffer)) result = send(ufb->ufbfh,buffer,strlen(buffer),0); else result = 0; } else { result = vfprintf(out,fmt,args); } RETURN(result); return(result); } int amiga_fprintf(FILE *out,const char *fmt,...) { va_list args; int result; va_start(args,fmt); result = amiga_vfprintf(out,fmt,args); va_end(args); return(result); } int amiga_printf(const char *fmt,...) { va_list args; int result; va_start(args,fmt); result = amiga_vfprintf(stdout,fmt,args); va_end(args); return(result); } size_t amiga_fwrite(const void *data,size_t blockSize,size_t numBlocks,FILE *out) { struct UFB * ufb; size_t result; chkabort(); ENTER(); ufb = chkufb(fileno(out)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { SHOWMSG("output to socket"); result = send(ufb->ufbfh,(APTR)data,blockSize * numBlocks,0); if(result > 0) result = (result/blockSize); } else { result = fwrite(data,blockSize,numBlocks,out); } RETURN(result); return(result); } size_t amiga_fread(void *data,size_t blockSize,size_t numBlocks,FILE *in) { struct UFB * ufb; size_t result; chkabort(); ENTER(); ufb = chkufb(fileno(in)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { SHOWMSG("input from socket"); result = recv(ufb->ufbfh,(APTR)data,blockSize * numBlocks,0); if(result > 0) result = (result/blockSize); } else { result = fread(data,blockSize,numBlocks,in); } RETURN(result); return(result); } int amiga_fclose(FILE * stream) { struct UFB * ufb; int result; chkabort(); ENTER(); ufb = chkufb(fileno(stream)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = OK; else result = fclose(stream); RETURN(result); return(result); } int amiga_fflush(FILE * stream) { struct UFB * ufb; int result; chkabort(); ENTER(); ufb = chkufb(fileno(stream)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = OK; else result = fflush(stream); RETURN(result); return(result); } int amiga_fseek(FILE * stream,long int offset,int mode) { struct UFB * ufb; int result; chkabort(); ENTER(); ufb = chkufb(fileno(stream)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = OK; else result = fseek(stream,offset,mode); RETURN(result); return(result); } long int amiga_ftell(FILE * stream) { struct UFB * ufb; long int result; chkabort(); ENTER(); ufb = chkufb(fileno(stream)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = 0; else result = ftell(stream); RETURN(result); return(result); } /******************************************************************************/ int amiga_setvbuf(FILE *stream,char *buff,int type,size_t size) { struct UFB * ufb; int result; chkabort(); ENTER(); ufb = chkufb(fileno(stream)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = OK; else result = setvbuf(stream,buff,type,size); RETURN(result); return(result); } /******************************************************************************/ int amiga_fputc(int c,FILE *stream) { struct UFB * ufb; int result = ERROR; chkabort(); ENTER(); ufb = chkufb(fileno(stream)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { UBYTE oneByte = c; if(send(ufb->ufbfh,&oneByte,1,0) > 0) result = OK; } else { result = fputc(c,stream); } RETURN(result); return(result); } /******************************************************************************/ VOID amiga_setbuf(FILE *stream,char *buffer) { struct UFB * ufb; chkabort(); ENTER(); ufb = chkufb(fileno(stream)); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) { /* Do nothing */ } else { setbuf(stream,buffer); } LEAVE(); } /******************************************************************************/ int amiga_recv(int fd,void *buff,size_t nbytes,int flags) { struct UFB * ufb; int result = ERROR; ENTER(); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = recv(ufb->ufbfh,buff,nbytes,flags); else errno = EBADF; RETURN(result); return(result); } /******************************************************************************/ int amiga_send(int fd,void *buff,size_t nbytes,int flags) { struct UFB * ufb; int result = ERROR; ENTER(); ufb = chkufb(fd); if(ufb != NULL && FLAG_IS_SET(ufb->ufbflg,UFB_IS_SOCKET)) result = send(ufb->ufbfh,buff,nbytes,flags); else errno = EBADF; RETURN(result); return(result); } /******************************************************************************/ int amiga_smbrun(char *cmd,char *outfile,BOOL shared) { struct MangleInfo mi_cmd; struct MangleInfo mi_outfile; int result = ERROR; ENTER(); ForbidDOS(); if(outfile == NULL) outfile = "/dev/null"; if(MangleName(&cmd,&mi_cmd) == OK) { if(MangleName(&outfile,&mi_outfile) == OK) { BPTR in; in = Open("NIL:",MODE_OLDFILE); if(in != ZERO) { BPTR out; out = Open(outfile,MODE_NEWFILE); if(out != ZERO) { if(SystemTags(cmd, SYS_Input, in, SYS_Output, out, SYS_UserShell, TRUE, NP_WindowPtr, NULL, TAG_DONE) != -1) { result = OK; } else { MapIoErrToErrno(); } Close(out); } else { MapIoErrToErrno(); } Close(in); } else { MapIoErrToErrno(); } UnmangleName(&outfile,&mi_outfile); } UnmangleName(&cmd,&mi_cmd); } PermitDOS(); RETURN(result); return(result); } /******************************************************************************/ #define IFBSIZE 1024 #define max(a,b) ( (a) > (b) ? (a) : (b) ) int amiga_get_interfaces(struct iface_struct * ifaces,int max_interfaces) { struct ifreq * ifr_end; struct ifreq * ifr; struct ifconf * ifc; struct ifreq ifr_copy; int sockfd = -1; int result = -1; int total = 0; int len; /* Make room for the interface information. I hope * that 1024 bytes will be sufficient. */ ifc = malloc(sizeof(*ifc) + IFBSIZE); if(ifc == NULL) { errno = ENOMEM; goto out; } sockfd = socket(AF_INET,SOCK_STREAM,0); if(sockfd < 0) goto out; /* Now attempt to copy the interface information into * the buffer. As of this writing, support for the * SIOCGIFCONF ioctl() action is undocumented in all * currently existing TCP/IP stacks. Nevertheless, * it appears to work. */ ifc->ifc_len = IFBSIZE; ifc->ifc_buf = (char *)(ifc+1); if(IoctlSocket(sockfd,SIOCGIFCONF,(char *)ifc) != OK) goto out; len = ifc->ifc_len; ifr = (struct ifreq *)ifc->ifc_buf; ifr_end = (struct ifreq *)((char *)ifr + len); /* Now check each interface, extracting the interface * information. */ while(ifr < ifr_end && total < max_interfaces) { ifr_copy = (*ifr); /* Try to obtain the address information. */ if(IoctlSocket(sockfd,SIOCGIFADDR,(char *)&ifr_copy) == OK) { struct in_addr ipaddr; /* We need to remember this for later. */ ipaddr = (*(struct sockaddr_in *)&ifr_copy.ifr_addr).sin_addr; /* And query the interface flags; in particular, we are interested * in whether this interface is currrently "up", i.e. "online". */ if(IoctlSocket(sockfd,SIOCGIFFLAGS,(char *)&ifr_copy) == OK) { if(ifr_copy.ifr_flags & IFF_UP) { /* And finally obtain the interface net mask. */ if(IoctlSocket(sockfd,SIOCGIFNETMASK,(char *)&ifr_copy) == OK) { struct in_addr nmask; nmask = ((struct sockaddr_in *)&ifr_copy.ifr_addr)->sin_addr; strncpy(ifaces[total].name, ifr_copy.ifr_name, sizeof(ifaces[total].name)-1); ifaces[total].name[sizeof(ifaces[total].name)-1] = '\0'; ifaces[total].ip = ipaddr; ifaces[total].netmask = nmask; total++; } } } } len = max(sizeof(struct sockaddr),ifr->ifr_addr.sa_len); ifr = (struct ifreq *)(((char *)ifr) + sizeof(ifr->ifr_name) + len); } result = total; out: if(ifc != NULL) free(ifc); if(sockfd != -1) CloseSocket(sockfd); return(result); } /******************************************************************************/ FILE * amiga_popen(const char * command,const char * mode) { /* For now, nothing happens here. */ errno = EPIPE; return(NULL); } int amiga_pclose(FILE * fp) { /* Almost twice as much happens here. */ return(OK); }